Merge branch '2.2'
* 2.2:
[HttpFoundation] fixed Request::create() method
[HttpKernel] fixed the creation of the Profiler directory
[HttpKernel] fixed the hinclude fragment renderer when the template is empty
bumped Symfony version to 2.2.0-RC2-DEV
[DependencyInjection] enhanced some error messages
[FrameworkBundle] fixed typo
fixed typo
tweaked previous merge
[Security] fixed interface implementation (closes #6974)
Add "'property_path' => false" deprecation message for forms
fixed CS
Added BCrypt password encoder.
updated VERSION for 2.2.0-RC1
Removed underscores from test method names to be consistent with other components.
[Security] fixed session creation when none is needed (closes #6917)
[FrameworkBundle] removed obsolete comment (see 2e356c1
)
Micro-optimization
[FrameworkBundle] removed extra whitespaces
[Security] renamed Constraint namespace to Constraints for validator classes in order to be consistent with the whole current validator API.
[FrameworkBundle] fixed wrong indentation on route debug output
This commit is contained in:
commit
8df773201a
@ -567,6 +567,55 @@
|
|||||||
trusted_proxies: ['127.0.0.1', '10.0.0.1'] # a list of proxy IPs you trust
|
trusted_proxies: ['127.0.0.1', '10.0.0.1'] # a list of proxy IPs you trust
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
* The existing ``UserPassword`` validator constraint class has been modified.
|
||||||
|
Its namespace has been changed to better fit the Symfony coding conventions.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraint\UserPassword;
|
||||||
|
```
|
||||||
|
|
||||||
|
After: (note the `s` at the end of `Constraint`)
|
||||||
|
|
||||||
|
```
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
|
||||||
|
```
|
||||||
|
|
||||||
|
* The new ``UserPassword`` validator constraint class now accepts a new
|
||||||
|
``service`` option that allows to specify a custom validator service name in
|
||||||
|
order to validate the current logged-in user's password.
|
||||||
|
|
||||||
|
```
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
|
||||||
|
|
||||||
|
$constraint = new UserPassword(array(
|
||||||
|
'service' => 'my.custom.validator.user_password',
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deprecations
|
||||||
|
|
||||||
|
* The two previous ``UserPassword`` and ``UserPasswordValidator`` classes in
|
||||||
|
the ``Symfony\Component\Security\Core\Validator\Constraint`` namespace have
|
||||||
|
been deprecated and will be removed in 2.3.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraint\UserPassword;
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraint\UserPasswordValidator;
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
|
||||||
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator;
|
||||||
|
```
|
||||||
|
|
||||||
### Serializer
|
### Serializer
|
||||||
|
|
||||||
* All serializer interfaces (Serializer, Normalizer, Encoder) have been
|
* All serializer interfaces (Serializer, Normalizer, Encoder) have been
|
||||||
|
@ -128,10 +128,10 @@ EOF
|
|||||||
|
|
||||||
$output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
|
$output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
|
||||||
|
|
||||||
$output->writeln(sprintf('<comment>Name</comment> %s', $name));
|
$output->writeln(sprintf('<comment>Name</comment> %s', $name));
|
||||||
$output->writeln(sprintf('<comment>Pattern</comment> %s', $route->getPath()));
|
$output->writeln(sprintf('<comment>Pattern</comment> %s', $route->getPath()));
|
||||||
$output->writeln(sprintf('<comment>Host</comment> %s', $host));
|
$output->writeln(sprintf('<comment>Host</comment> %s', $host));
|
||||||
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($route)));
|
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($route)));
|
||||||
|
|
||||||
$defaults = '';
|
$defaults = '';
|
||||||
$d = $route->getDefaults();
|
$d = $route->getDefaults();
|
||||||
@ -139,7 +139,7 @@ EOF
|
|||||||
foreach ($d as $name => $value) {
|
foreach ($d as $name => $value) {
|
||||||
$defaults .= ($defaults ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
$defaults .= ($defaults ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
||||||
}
|
}
|
||||||
$output->writeln(sprintf('<comment>Defaults</comment> %s', $defaults));
|
$output->writeln(sprintf('<comment>Defaults</comment> %s', $defaults));
|
||||||
|
|
||||||
$requirements = '';
|
$requirements = '';
|
||||||
$r = $route->getRequirements();
|
$r = $route->getRequirements();
|
||||||
@ -148,7 +148,7 @@ EOF
|
|||||||
$requirements .= ($requirements ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
$requirements .= ($requirements ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
||||||
}
|
}
|
||||||
$requirements = '' !== $requirements ? $requirements : 'NONE';
|
$requirements = '' !== $requirements ? $requirements : 'NONE';
|
||||||
$output->writeln(sprintf('<comment>Requirements</comment> %s', $requirements));
|
$output->writeln(sprintf('<comment>Requirements</comment> %s', $requirements));
|
||||||
|
|
||||||
$options = '';
|
$options = '';
|
||||||
$o = $route->getOptions();
|
$o = $route->getOptions();
|
||||||
@ -156,8 +156,8 @@ EOF
|
|||||||
foreach ($o as $name => $value) {
|
foreach ($o as $name => $value) {
|
||||||
$options .= ($options ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
$options .= ($options ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
|
||||||
}
|
}
|
||||||
$output->writeln(sprintf('<comment>Options</comment> %s', $options));
|
$output->writeln(sprintf('<comment>Options</comment> %s', $options));
|
||||||
$output->write('<comment>Regex</comment> ');
|
$output->write('<comment>Regex</comment> ');
|
||||||
$output->writeln(preg_replace('/^ /', '', preg_replace('/^/m', ' ', $route->compile()->getRegex())), OutputInterface::OUTPUT_RAW);
|
$output->writeln(preg_replace('/^ /', '', preg_replace('/^/m', ' ', $route->compile()->getRegex())), OutputInterface::OUTPUT_RAW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,9 +21,6 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|||||||
/**
|
/**
|
||||||
* Sets the session in the request.
|
* Sets the session in the request.
|
||||||
*
|
*
|
||||||
* This will also start the session if it was already started during a previous
|
|
||||||
* request.
|
|
||||||
*
|
|
||||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
*/
|
*/
|
||||||
class SessionListener implements EventSubscriberInterface
|
class SessionListener implements EventSubscriberInterface
|
||||||
|
@ -67,11 +67,11 @@ class HttpKernel extends ContainerAwareHttpKernel
|
|||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*
|
*
|
||||||
* @deprecated in 2.2, will be removed in 2.3 (use Symfony\Component\HttpKernel\FragmentRenderer::render() instead)
|
* @deprecated in 2.2, will be removed in 2.3 (use Symfony\Component\HttpKernel\Fragment\FragmentHandler::render() instead)
|
||||||
*/
|
*/
|
||||||
public function render($uri, array $options = array())
|
public function render($uri, array $options = array())
|
||||||
{
|
{
|
||||||
trigger_error('render() is deprecated since version 2.2 and will be removed in 2.3. Use Symfony\Component\HttpKernel\FragmentRenderer::render() instead.', E_USER_DEPRECATED);
|
trigger_error('render() is deprecated since version 2.2 and will be removed in 2.3. Use Symfony\Component\HttpKernel\Fragment\FragmentHandler::render() instead.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
$options = $this->renderer->fixOptions($options);
|
$options = $this->renderer->fixOptions($options);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ CHANGELOG
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
* Added PBKDF2 Password encoder
|
* Added PBKDF2 Password encoder
|
||||||
|
* Added BCrypt password encoder
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
@ -383,6 +383,11 @@ class MainConfiguration implements ConfigurationInterface
|
|||||||
->booleanNode('ignore_case')->defaultFalse()->end()
|
->booleanNode('ignore_case')->defaultFalse()->end()
|
||||||
->booleanNode('encode_as_base64')->defaultTrue()->end()
|
->booleanNode('encode_as_base64')->defaultTrue()->end()
|
||||||
->scalarNode('iterations')->defaultValue(5000)->end()
|
->scalarNode('iterations')->defaultValue(5000)->end()
|
||||||
|
->integerNode('cost')
|
||||||
|
->min(4)
|
||||||
|
->max(31)
|
||||||
|
->defaultValue(13)
|
||||||
|
->end()
|
||||||
->scalarNode('id')->end()
|
->scalarNode('id')->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
|
|||||||
|
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||||
use Symfony\Component\DependencyInjection\Alias;
|
use Symfony\Component\DependencyInjection\Alias;
|
||||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||||
@ -464,6 +465,19 @@ class SecurityExtension extends Extension
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bcrypt encoder
|
||||||
|
if ('bcrypt' === $config['algorithm']) {
|
||||||
|
$arguments = array(
|
||||||
|
new Reference('security.secure_random'),
|
||||||
|
$config['cost'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'class' => new Parameter('security.encoder.bcrypt.class'),
|
||||||
|
'arguments' => $arguments,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// message digest encoder
|
// message digest encoder
|
||||||
$arguments = array(
|
$arguments = array(
|
||||||
$config['algorithm'],
|
$config['algorithm'],
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<parameter key="security.encoder.digest.class">Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder</parameter>
|
<parameter key="security.encoder.digest.class">Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder</parameter>
|
||||||
<parameter key="security.encoder.plain.class">Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder</parameter>
|
<parameter key="security.encoder.plain.class">Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder</parameter>
|
||||||
<parameter key="security.encoder.pbkdf2.class">Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder</parameter>
|
<parameter key="security.encoder.pbkdf2.class">Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder</parameter>
|
||||||
|
<parameter key="security.encoder.bcrypt.class">Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder</parameter>
|
||||||
|
|
||||||
<parameter key="security.user.provider.in_memory.class">Symfony\Component\Security\Core\User\InMemoryUserProvider</parameter>
|
<parameter key="security.user.provider.in_memory.class">Symfony\Component\Security\Core\User\InMemoryUserProvider</parameter>
|
||||||
<parameter key="security.user.provider.in_memory.user.class">Symfony\Component\Security\Core\User\User</parameter>
|
<parameter key="security.user.provider.in_memory.user.class">Symfony\Component\Security\Core\User\User</parameter>
|
||||||
@ -41,7 +42,7 @@
|
|||||||
|
|
||||||
<parameter key="security.http_utils.class">Symfony\Component\Security\Http\HttpUtils</parameter>
|
<parameter key="security.http_utils.class">Symfony\Component\Security\Http\HttpUtils</parameter>
|
||||||
|
|
||||||
<parameter key="security.validator.user_password.class">Symfony\Component\Security\Core\Validator\Constraint\UserPasswordValidator</parameter>
|
<parameter key="security.validator.user_password.class">Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
|
@ -22,6 +22,10 @@ $container->loadFromExtension('security', array(
|
|||||||
'iterations' => 5,
|
'iterations' => 5,
|
||||||
'key_length' => 30,
|
'key_length' => 30,
|
||||||
),
|
),
|
||||||
|
'JMS\FooBundle\Entity\User6' => array(
|
||||||
|
'algorithm' => 'bcrypt',
|
||||||
|
'cost' => 15,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'providers' => array(
|
'providers' => array(
|
||||||
'default' => array(
|
'default' => array(
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
<encoder class="JMS\FooBundle\Entity\User5" algorithm="pbkdf2" hash-algorithm="sha1" encode-as-base64="false" iterations="5" key-length="30" />
|
<encoder class="JMS\FooBundle\Entity\User5" algorithm="pbkdf2" hash-algorithm="sha1" encode-as-base64="false" iterations="5" key-length="30" />
|
||||||
|
|
||||||
|
<encoder class="JMS\FooBundle\Entity\User6" algorithm="bcrypt" cost="15" />
|
||||||
|
|
||||||
<provider name="default">
|
<provider name="default">
|
||||||
<memory>
|
<memory>
|
||||||
<user name="foo" password="foo" roles="ROLE_USER" />
|
<user name="foo" password="foo" roles="ROLE_USER" />
|
||||||
|
@ -16,6 +16,9 @@ security:
|
|||||||
encode_as_base64: false
|
encode_as_base64: false
|
||||||
iterations: 5
|
iterations: 5
|
||||||
key_length: 30
|
key_length: 30
|
||||||
|
JMS\FooBundle\Entity\User6:
|
||||||
|
algorithm: bcrypt
|
||||||
|
cost: 15
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
default:
|
default:
|
||||||
|
@ -158,6 +158,13 @@ abstract class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
|
|||||||
'class' => new Parameter('security.encoder.pbkdf2.class'),
|
'class' => new Parameter('security.encoder.pbkdf2.class'),
|
||||||
'arguments' => array('sha1', false, 5, 30),
|
'arguments' => array('sha1', false, 5, 30),
|
||||||
),
|
),
|
||||||
|
'JMS\FooBundle\Entity\User6' => array(
|
||||||
|
'class' => new Parameter('security.encoder.bcrypt.class'),
|
||||||
|
'arguments' => array(
|
||||||
|
new Reference('security.secure_random'),
|
||||||
|
15,
|
||||||
|
)
|
||||||
|
),
|
||||||
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
|
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,14 +192,14 @@ class Container implements IntrospectableContainerInterface
|
|||||||
public function set($id, $service, $scope = self::SCOPE_CONTAINER)
|
public function set($id, $service, $scope = self::SCOPE_CONTAINER)
|
||||||
{
|
{
|
||||||
if (self::SCOPE_PROTOTYPE === $scope) {
|
if (self::SCOPE_PROTOTYPE === $scope) {
|
||||||
throw new InvalidArgumentException('You cannot set services of scope "prototype".');
|
throw new InvalidArgumentException(sprintf('You cannot set service "%s" of scope "prototype".', $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = strtolower($id);
|
$id = strtolower($id);
|
||||||
|
|
||||||
if (self::SCOPE_CONTAINER !== $scope) {
|
if (self::SCOPE_CONTAINER !== $scope) {
|
||||||
if (!isset($this->scopedServices[$scope])) {
|
if (!isset($this->scopedServices[$scope])) {
|
||||||
throw new RuntimeException('You cannot set services of inactive scopes.');
|
throw new RuntimeException(sprintf('You cannot set service "%s" of inactive scope.', $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->scopedServices[$scope][$id] = $service;
|
$this->scopedServices[$scope][$id] = $service;
|
||||||
|
@ -857,7 +857,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
|||||||
} elseif (null !== $definition->getFactoryService()) {
|
} elseif (null !== $definition->getFactoryService()) {
|
||||||
$factory = $this->get($parameterBag->resolveValue($definition->getFactoryService()));
|
$factory = $this->get($parameterBag->resolveValue($definition->getFactoryService()));
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException('Cannot create service from factory method without a factory service or factory class.');
|
throw new RuntimeException(sprintf('Cannot create service "%s" from factory method without a factory service or factory class.', $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments);
|
$service = call_user_func_array(array($factory, $definition->getFactoryMethod()), $arguments);
|
||||||
@ -869,7 +869,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
|||||||
|
|
||||||
if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
|
if (self::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
|
||||||
if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) {
|
if (self::SCOPE_CONTAINER !== $scope && !isset($this->scopedServices[$scope])) {
|
||||||
throw new RuntimeException('You tried to create a service of an inactive scope.');
|
throw new RuntimeException(sprintf('You tried to create the "%s" service of an inactive scope.', $id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->services[$lowerId = strtolower($id)] = $service;
|
$this->services[$lowerId = strtolower($id)] = $service;
|
||||||
|
@ -746,7 +746,7 @@ EOF;
|
|||||||
{
|
{
|
||||||
\$name = strtolower(\$name);
|
\$name = strtolower(\$name);
|
||||||
|
|
||||||
if (!array_key_exists(\$name, \$this->parameters)) {
|
if (!(isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) {
|
||||||
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
|
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,7 +758,9 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
public function hasParameter(\$name)
|
public function hasParameter(\$name)
|
||||||
{
|
{
|
||||||
return array_key_exists(strtolower(\$name), \$this->parameters);
|
\$name = strtolower(\$name);
|
||||||
|
|
||||||
|
return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +55,7 @@ class ProjectServiceContainer extends Container
|
|||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
|
|
||||||
if (!array_key_exists($name, $this->parameters)) {
|
if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
|
||||||
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
|
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,9 @@ class ProjectServiceContainer extends Container
|
|||||||
*/
|
*/
|
||||||
public function hasParameter($name)
|
public function hasParameter($name)
|
||||||
{
|
{
|
||||||
return array_key_exists(strtolower($name), $this->parameters);
|
$name = strtolower($name);
|
||||||
|
|
||||||
|
return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,7 +191,7 @@ class ProjectServiceContainer extends Container
|
|||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
|
|
||||||
if (!array_key_exists($name, $this->parameters)) {
|
if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) {
|
||||||
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
|
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +203,9 @@ class ProjectServiceContainer extends Container
|
|||||||
*/
|
*/
|
||||||
public function hasParameter($name)
|
public function hasParameter($name)
|
||||||
{
|
{
|
||||||
return array_key_exists(strtolower($name), $this->parameters);
|
$name = strtolower($name);
|
||||||
|
|
||||||
|
return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,6 +56,10 @@ class FormType extends AbstractType
|
|||||||
->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
|
->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
if (false === $options['property_path']) {
|
||||||
|
trigger_error('Setting "property_path" to "false" is deprecated since version 2.1 and will be removed in 2.3. Set "mapped" to "false" instead.', E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
if ($options['trim']) {
|
if ($options['trim']) {
|
||||||
$builder->addEventSubscriber(new TrimListener());
|
$builder->addEventSubscriber(new TrimListener());
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ CHANGELOG
|
|||||||
2.2.0
|
2.2.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* fixed the Request::create() precedence (URI information always take precedence now)
|
||||||
* added Request::getTrustedProxies()
|
* added Request::getTrustedProxies()
|
||||||
* deprecated Request::isProxyTrusted()
|
* deprecated Request::isProxyTrusted()
|
||||||
* [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects
|
* [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects
|
||||||
|
@ -253,6 +253,9 @@ class Request
|
|||||||
/**
|
/**
|
||||||
* Creates a Request based on a given URI and configuration.
|
* Creates a Request based on a given URI and configuration.
|
||||||
*
|
*
|
||||||
|
* The information contained in the URI always take precedence
|
||||||
|
* over the other information (server and parameters).
|
||||||
|
*
|
||||||
* @param string $uri The URI
|
* @param string $uri The URI
|
||||||
* @param string $method The HTTP method
|
* @param string $method The HTTP method
|
||||||
* @param array $parameters The query (GET) or request (POST) parameters
|
* @param array $parameters The query (GET) or request (POST) parameters
|
||||||
@ -267,7 +270,7 @@ class Request
|
|||||||
*/
|
*/
|
||||||
public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
|
public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
|
||||||
{
|
{
|
||||||
$defaults = array(
|
$server = array_replace(array(
|
||||||
'SERVER_NAME' => 'localhost',
|
'SERVER_NAME' => 'localhost',
|
||||||
'SERVER_PORT' => 80,
|
'SERVER_PORT' => 80,
|
||||||
'HTTP_HOST' => 'localhost',
|
'HTTP_HOST' => 'localhost',
|
||||||
@ -280,32 +283,38 @@ class Request
|
|||||||
'SCRIPT_FILENAME' => '',
|
'SCRIPT_FILENAME' => '',
|
||||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||||
'REQUEST_TIME' => time(),
|
'REQUEST_TIME' => time(),
|
||||||
);
|
), $server);
|
||||||
|
|
||||||
|
$server['PATH_INFO'] = '';
|
||||||
|
$server['REQUEST_METHOD'] = strtoupper($method);
|
||||||
|
|
||||||
$components = parse_url($uri);
|
$components = parse_url($uri);
|
||||||
if (isset($components['host'])) {
|
if (isset($components['host'])) {
|
||||||
$defaults['SERVER_NAME'] = $components['host'];
|
$server['SERVER_NAME'] = $components['host'];
|
||||||
$defaults['HTTP_HOST'] = $components['host'];
|
$server['HTTP_HOST'] = $components['host'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($components['scheme'])) {
|
if (isset($components['scheme'])) {
|
||||||
if ('https' === $components['scheme']) {
|
if ('https' === $components['scheme']) {
|
||||||
$defaults['HTTPS'] = 'on';
|
$server['HTTPS'] = 'on';
|
||||||
$defaults['SERVER_PORT'] = 443;
|
$server['SERVER_PORT'] = 443;
|
||||||
|
} else {
|
||||||
|
unset($server['HTTPS']);
|
||||||
|
$server['SERVER_PORT'] = 80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($components['port'])) {
|
if (isset($components['port'])) {
|
||||||
$defaults['SERVER_PORT'] = $components['port'];
|
$server['SERVER_PORT'] = $components['port'];
|
||||||
$defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port'];
|
$server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($components['user'])) {
|
if (isset($components['user'])) {
|
||||||
$defaults['PHP_AUTH_USER'] = $components['user'];
|
$server['PHP_AUTH_USER'] = $components['user'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($components['pass'])) {
|
if (isset($components['pass'])) {
|
||||||
$defaults['PHP_AUTH_PW'] = $components['pass'];
|
$server['PHP_AUTH_PW'] = $components['pass'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($components['path'])) {
|
if (!isset($components['path'])) {
|
||||||
@ -316,7 +325,7 @@ class Request
|
|||||||
case 'POST':
|
case 'POST':
|
||||||
case 'PUT':
|
case 'PUT':
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
$defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
$server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
||||||
case 'PATCH':
|
case 'PATCH':
|
||||||
$request = $parameters;
|
$request = $parameters;
|
||||||
$query = array();
|
$query = array();
|
||||||
@ -333,14 +342,8 @@ class Request
|
|||||||
}
|
}
|
||||||
$queryString = http_build_query($query, '', '&');
|
$queryString = http_build_query($query, '', '&');
|
||||||
|
|
||||||
$uri = $components['path'].('' !== $queryString ? '?'.$queryString : '');
|
$server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
|
||||||
|
$server['QUERY_STRING'] = $queryString;
|
||||||
$server = array_replace($defaults, $server, array(
|
|
||||||
'REQUEST_METHOD' => strtoupper($method),
|
|
||||||
'PATH_INFO' => '',
|
|
||||||
'REQUEST_URI' => $uri,
|
|
||||||
'QUERY_STRING' => $queryString,
|
|
||||||
));
|
|
||||||
|
|
||||||
return new static($query, $request, array(), $cookies, $files, $server, $content);
|
return new static($query, $request, array(), $cookies, $files, $server, $content);
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,41 @@ class RequestTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertFalse($request->isSecure());
|
$this->assertFalse($request->isSecure());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Symfony\Component\HttpFoundation\Request::create
|
||||||
|
*/
|
||||||
|
public function testCreateCheckPrecedence()
|
||||||
|
{
|
||||||
|
// server is used by default
|
||||||
|
$request = Request::create('/', 'GET', array(), array(), array(), array(
|
||||||
|
'HTTP_HOST' => 'example.com',
|
||||||
|
'HTTPS' => 'on',
|
||||||
|
'SERVER_PORT' => 443,
|
||||||
|
'PHP_AUTH_USER' => 'fabien',
|
||||||
|
'PHP_AUTH_PW' => 'pa$$',
|
||||||
|
'QUERY_STRING' => 'foo=bar',
|
||||||
|
));
|
||||||
|
$this->assertEquals('example.com', $request->getHost());
|
||||||
|
$this->assertEquals(443, $request->getPort());
|
||||||
|
$this->assertTrue($request->isSecure());
|
||||||
|
$this->assertEquals('fabien', $request->getUser());
|
||||||
|
$this->assertEquals('pa$$', $request->getPassword());
|
||||||
|
$this->assertEquals('', $request->getQueryString());
|
||||||
|
|
||||||
|
// URI has precedence over server
|
||||||
|
$request = Request::create('http://thomas:pokemon@example.net:8080/?foo=bar', 'GET', array(), array(), array(), array(
|
||||||
|
'HTTP_HOST' => 'example.com',
|
||||||
|
'HTTPS' => 'on',
|
||||||
|
'SERVER_PORT' => 443,
|
||||||
|
));
|
||||||
|
$this->assertEquals('example.net', $request->getHost());
|
||||||
|
$this->assertEquals(8080, $request->getPort());
|
||||||
|
$this->assertFalse($request->isSecure());
|
||||||
|
$this->assertEquals('thomas', $request->getUser());
|
||||||
|
$this->assertEquals('pokemon', $request->getPassword());
|
||||||
|
$this->assertEquals('foo=bar', $request->getQueryString());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Symfony\Component\HttpFoundation\Request::duplicate
|
* @covers Symfony\Component\HttpFoundation\Request::duplicate
|
||||||
*/
|
*/
|
||||||
|
@ -65,7 +65,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
$template = isset($options['default']) ? $options['default'] : $this->globalDefaultTemplate;
|
$template = isset($options['default']) ? $options['default'] : $this->globalDefaultTemplate;
|
||||||
if (null !== $this->templating && $this->templateExists($template)) {
|
if (null !== $this->templating && $template && $this->templateExists($template)) {
|
||||||
$content = $this->templating->render($template);
|
$content = $this->templating->render($template);
|
||||||
} else {
|
} else {
|
||||||
$content = $template;
|
$content = $template;
|
||||||
|
@ -62,12 +62,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface
|
|||||||
protected $classes;
|
protected $classes;
|
||||||
protected $errorReportingLevel;
|
protected $errorReportingLevel;
|
||||||
|
|
||||||
const VERSION = '2.2.0-DEV';
|
const VERSION = '2.2.0-RC2';
|
||||||
const VERSION_ID = '20100';
|
const VERSION_ID = '20100';
|
||||||
const MAJOR_VERSION = '2';
|
const MAJOR_VERSION = '2';
|
||||||
const MINOR_VERSION = '2';
|
const MINOR_VERSION = '2';
|
||||||
const RELEASE_VERSION = '0';
|
const RELEASE_VERSION = '0';
|
||||||
const EXTRA_VERSION = 'DEV';
|
const EXTRA_VERSION = 'RC2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -41,7 +41,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
|
|||||||
$this->folder = substr($dsn, 5);
|
$this->folder = substr($dsn, 5);
|
||||||
|
|
||||||
if (!is_dir($this->folder)) {
|
if (!is_dir($this->folder)) {
|
||||||
mkdir($this->folder);
|
mkdir($this->folder, 0777, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||||
*/
|
*/
|
||||||
public function testInvalidPropertyPath_noDotBeforeProperty()
|
public function testDotIsRequiredBeforeProperty()
|
||||||
{
|
{
|
||||||
new PropertyPath('[index]property');
|
new PropertyPath('[index]property');
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||||
*/
|
*/
|
||||||
public function testInvalidPropertyPath_dotAtTheBeginning()
|
public function testDotCannotBePresentAtTheBeginning()
|
||||||
{
|
{
|
||||||
new PropertyPath('.property');
|
new PropertyPath('.property');
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||||
*/
|
*/
|
||||||
public function testInvalidPropertyPath_unexpectedCharacters()
|
public function testUnexpectedCharacters()
|
||||||
{
|
{
|
||||||
new PropertyPath('property.$foo');
|
new PropertyPath('property.$foo');
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException
|
||||||
*/
|
*/
|
||||||
public function testInvalidPropertyPath_empty()
|
public function testPathCannotBeEmpty()
|
||||||
{
|
{
|
||||||
new PropertyPath('');
|
new PropertyPath('');
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
||||||
*/
|
*/
|
||||||
public function testInvalidPropertyPath_null()
|
public function testPathCannotBeNull()
|
||||||
{
|
{
|
||||||
new PropertyPath(null);
|
new PropertyPath(null);
|
||||||
}
|
}
|
||||||
@ -67,31 +67,31 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
* @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException
|
||||||
*/
|
*/
|
||||||
public function testInvalidPropertyPath_false()
|
public function testPathCannotBeFalse()
|
||||||
{
|
{
|
||||||
new PropertyPath(false);
|
new PropertyPath(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testValidPropertyPath_zero()
|
public function testZeroIsValidPropertyPath()
|
||||||
{
|
{
|
||||||
new PropertyPath('0');
|
new PropertyPath('0');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetParent_dot()
|
public function testGetParentWithDot()
|
||||||
{
|
{
|
||||||
$propertyPath = new PropertyPath('grandpa.parent.child');
|
$propertyPath = new PropertyPath('grandpa.parent.child');
|
||||||
|
|
||||||
$this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
|
$this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetParent_index()
|
public function testGetParentWithIndex()
|
||||||
{
|
{
|
||||||
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
$propertyPath = new PropertyPath('grandpa.parent[child]');
|
||||||
|
|
||||||
$this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
|
$this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetParent_noParent()
|
public function testGetParentWhenThereIsNoParent()
|
||||||
{
|
{
|
||||||
$propertyPath = new PropertyPath('path');
|
$propertyPath = new PropertyPath('path');
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ CHANGELOG
|
|||||||
implements EventSubscriberInterface
|
implements EventSubscriberInterface
|
||||||
* added secure random number generator
|
* added secure random number generator
|
||||||
* added PBKDF2 Password encoder
|
* added PBKDF2 Password encoder
|
||||||
|
* added BCrypt password encoder
|
||||||
|
|
||||||
2.1.0
|
2.1.0
|
||||||
-----
|
-----
|
||||||
|
@ -0,0 +1,148 @@
|
|||||||
|
<?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\Encoder;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;
|
||||||
|
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Elnur Abdurrakhimov <elnur@elnur.pro>
|
||||||
|
* @author Terje Bråten <terje@braten.be>
|
||||||
|
*/
|
||||||
|
class BCryptPasswordEncoder extends BasePasswordEncoder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SecureRandomInterface
|
||||||
|
*/
|
||||||
|
private $secureRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cost;
|
||||||
|
|
||||||
|
private static $prefix = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param SecureRandomInterface $secureRandom A SecureRandomInterface instance
|
||||||
|
* @param integer $cost The algorithmic cost that should be used
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if cost is out of range
|
||||||
|
*/
|
||||||
|
public function __construct(SecureRandomInterface $secureRandom, $cost)
|
||||||
|
{
|
||||||
|
$this->secureRandom = $secureRandom;
|
||||||
|
|
||||||
|
$cost = (int) $cost;
|
||||||
|
if ($cost < 4 || $cost > 31) {
|
||||||
|
throw new \InvalidArgumentException('Cost must be in the range of 4-31.');
|
||||||
|
}
|
||||||
|
$this->cost = sprintf('%02d', $cost);
|
||||||
|
|
||||||
|
if (!self::$prefix) {
|
||||||
|
self::$prefix = '$'.(version_compare(phpversion(), '5.3.7', '>=') ? '2y' : '2a').'$';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function encodePassword($raw, $salt)
|
||||||
|
{
|
||||||
|
if (function_exists('password_hash')) {
|
||||||
|
return password_hash($raw, PASSWORD_BCRYPT, array('cost' => $this->cost));
|
||||||
|
}
|
||||||
|
|
||||||
|
$salt = self::$prefix.$this->cost.'$'.$this->encodeSalt($this->getRawSalt());
|
||||||
|
$encoded = crypt($raw, $salt);
|
||||||
|
if (!is_string($encoded) || strlen($encoded) <= 13) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isPasswordValid($encoded, $raw, $salt)
|
||||||
|
{
|
||||||
|
if (function_exists('password_verify')) {
|
||||||
|
return password_verify($raw, $encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
$crypted = crypt($raw, $encoded);
|
||||||
|
if (strlen($crypted) <= 13) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->comparePasswords($encoded, $crypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the salt to be used by Bcrypt.
|
||||||
|
*
|
||||||
|
* The blowfish/bcrypt algorithm used by PHP crypt expects a different
|
||||||
|
* set and order of characters than the usual base64_encode function.
|
||||||
|
* Regular b64: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
|
||||||
|
* Bcrypt b64: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
|
||||||
|
* We care because the last character in our encoded string will
|
||||||
|
* only represent 2 bits. While two known implementations of
|
||||||
|
* bcrypt will happily accept and correct a salt string which
|
||||||
|
* has the 4 unused bits set to non-zero, we do not want to take
|
||||||
|
* chances and we also do not want to waste an additional byte
|
||||||
|
* of entropy.
|
||||||
|
*
|
||||||
|
* @param bytes $random a string of 16 random bytes
|
||||||
|
*
|
||||||
|
* @return string Properly encoded salt to use with php crypt function
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if string of random bytes is too short
|
||||||
|
*/
|
||||||
|
protected function encodeSalt($random)
|
||||||
|
{
|
||||||
|
$len = strlen($random);
|
||||||
|
if ($len < 16) {
|
||||||
|
throw new \InvalidArgumentException('The bcrypt salt needs 16 random bytes.');
|
||||||
|
}
|
||||||
|
if ($len > 16) {
|
||||||
|
$random = substr($random, 0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
$base64raw = str_replace('+', '.', base64_encode($random));
|
||||||
|
$salt128bit = substr($base64raw, 0, 21);
|
||||||
|
$lastchar = substr($base64raw, 21, 1);
|
||||||
|
$lastchar = strtr($lastchar, 'AQgw', '.Oeu');
|
||||||
|
$salt128bit .= $lastchar;
|
||||||
|
|
||||||
|
return $salt128bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bytes 16 random bytes to be used in the salt
|
||||||
|
*/
|
||||||
|
protected function getRawSalt()
|
||||||
|
{
|
||||||
|
$rawSalt = false;
|
||||||
|
$numBytes = 16;
|
||||||
|
if (function_exists('mcrypt_create_iv')) {
|
||||||
|
$rawSalt = mcrypt_create_iv($numBytes, MCRYPT_DEV_URANDOM);
|
||||||
|
}
|
||||||
|
if (!$rawSalt) {
|
||||||
|
$rawSalt = $this->secureRandom->nextBytes($numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rawSalt;
|
||||||
|
}
|
||||||
|
}
|
@ -11,18 +11,19 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Security\Core\Validator\Constraint;
|
namespace Symfony\Component\Security\Core\Validator\Constraint;
|
||||||
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword as BaseUserPassword;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Annotation
|
* @Annotation
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated since version 2.2, to be removed in 2.3.
|
||||||
*/
|
*/
|
||||||
class UserPassword extends Constraint
|
class UserPassword extends BaseUserPassword
|
||||||
{
|
{
|
||||||
public $message = 'This value should be the user current password.';
|
public function __construct($options = null)
|
||||||
public $service = 'security.validator.user_password';
|
|
||||||
|
|
||||||
public function validatedBy()
|
|
||||||
{
|
{
|
||||||
return $this->service;
|
trigger_error('UserPassword class in Symfony\Component\Security\Core\Validator\Constraint namespace is deprecated since version 2.2 and will be removed in 2.3. Use the Symfony\Component\Security\Core\Validator\Constraints\UserPassword class instead.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
parent::__construct($options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,36 +11,19 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Security\Core\Validator\Constraint;
|
namespace Symfony\Component\Security\Core\Validator\Constraint;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
|
||||||
use Symfony\Component\Security\Core\SecurityContextInterface;
|
use Symfony\Component\Security\Core\SecurityContextInterface;
|
||||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator as BaseUserPasswordValidator;
|
||||||
use Symfony\Component\Validator\ConstraintValidator;
|
|
||||||
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
|
||||||
|
|
||||||
class UserPasswordValidator extends ConstraintValidator
|
/**
|
||||||
|
* @deprecated Deprecated since version 2.2, to be removed in 2.3.
|
||||||
|
*/
|
||||||
|
class UserPasswordValidator extends BaseUserPasswordValidator
|
||||||
{
|
{
|
||||||
private $securityContext;
|
|
||||||
private $encoderFactory;
|
|
||||||
|
|
||||||
public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
|
public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
|
||||||
{
|
{
|
||||||
$this->securityContext = $securityContext;
|
trigger_error('UserPasswordValidator class in Symfony\Component\Security\Core\Validator\Constraint namespace is deprecated since version 2.2 and will be removed in 2.3. Use the Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator class instead.', E_USER_DEPRECATED);
|
||||||
$this->encoderFactory = $encoderFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validate($password, Constraint $constraint)
|
parent::__construct($securityContext, $encoderFactory);
|
||||||
{
|
|
||||||
$user = $this->securityContext->getToken()->getUser();
|
|
||||||
|
|
||||||
if (!$user instanceof UserInterface) {
|
|
||||||
throw new ConstraintDefinitionException('The User must extend UserInterface');
|
|
||||||
}
|
|
||||||
|
|
||||||
$encoder = $this->encoderFactory->getEncoder($user);
|
|
||||||
|
|
||||||
if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
|
|
||||||
$this->context->addViolation($constraint->message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?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\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
*/
|
||||||
|
class UserPassword extends Constraint
|
||||||
|
{
|
||||||
|
public $message = 'This value should be the user current password.';
|
||||||
|
public $service = 'security.validator.user_password';
|
||||||
|
|
||||||
|
public function validatedBy()
|
||||||
|
{
|
||||||
|
return $this->service;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
<?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\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\SecurityContextInterface;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
|
|
||||||
|
class UserPasswordValidator extends ConstraintValidator
|
||||||
|
{
|
||||||
|
private $securityContext;
|
||||||
|
private $encoderFactory;
|
||||||
|
|
||||||
|
public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
|
||||||
|
{
|
||||||
|
$this->securityContext = $securityContext;
|
||||||
|
$this->encoderFactory = $encoderFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate($password, Constraint $constraint)
|
||||||
|
{
|
||||||
|
$user = $this->securityContext->getToken()->getUser();
|
||||||
|
|
||||||
|
if (!$user instanceof UserInterface) {
|
||||||
|
throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$encoder = $this->encoderFactory->getEncoder($user);
|
||||||
|
|
||||||
|
if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
|
||||||
|
$this->context->addViolation($constraint->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -70,7 +70,6 @@ class ContextListener implements ListenerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$request = $event->getRequest();
|
$request = $event->getRequest();
|
||||||
|
|
||||||
$session = $request->hasPreviousSession() ? $request->getSession() : null;
|
$session = $request->hasPreviousSession() ? $request->getSession() : null;
|
||||||
|
|
||||||
if (null === $session || null === $token = $session->get('_security_'.$this->contextKey)) {
|
if (null === $session || null === $token = $session->get('_security_'.$this->contextKey)) {
|
||||||
@ -117,7 +116,10 @@ class ContextListener implements ListenerInterface
|
|||||||
$this->logger->debug('Write SecurityContext in the session');
|
$this->logger->debug('Write SecurityContext in the session');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $session = $event->getRequest()->getSession()) {
|
$request = $event->getRequest();
|
||||||
|
$session = $request->hasPreviousSession() ? $request->getSession() : null;
|
||||||
|
|
||||||
|
if (null === $session) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
<?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\Core\Encoder;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Elnur Abdurrakhimov <elnur@elnur.pro>
|
||||||
|
*/
|
||||||
|
class BCryptPasswordEncoderTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
const PASSWORD = 'password';
|
||||||
|
const BYTES = '0123456789abcdef';
|
||||||
|
const VALID_COST = '04';
|
||||||
|
|
||||||
|
const SECURE_RANDOM_INTERFACE = 'Symfony\Component\Security\Core\Util\SecureRandomInterface';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $secureRandom;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->secureRandom = $this->getMock(self::SECURE_RANDOM_INTERFACE);
|
||||||
|
|
||||||
|
$this->secureRandom
|
||||||
|
->expects($this->any())
|
||||||
|
->method('nextBytes')
|
||||||
|
->will($this->returnValue(self::BYTES))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function testCostBelowRange()
|
||||||
|
{
|
||||||
|
new BCryptPasswordEncoder($this->secureRandom, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function testCostAboveRange()
|
||||||
|
{
|
||||||
|
new BCryptPasswordEncoder($this->secureRandom, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCostInRange()
|
||||||
|
{
|
||||||
|
for ($cost = 4; $cost <= 31; $cost++) {
|
||||||
|
new BCryptPasswordEncoder($this->secureRandom, $cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResultLength()
|
||||||
|
{
|
||||||
|
$encoder = new BCryptPasswordEncoder($this->secureRandom, self::VALID_COST);
|
||||||
|
$result = $encoder->encodePassword(self::PASSWORD, null);
|
||||||
|
$this->assertEquals(60, strlen($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidation()
|
||||||
|
{
|
||||||
|
$encoder = new BCryptPasswordEncoder($this->secureRandom, self::VALID_COST);
|
||||||
|
$result = $encoder->encodePassword(self::PASSWORD, null);
|
||||||
|
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null));
|
||||||
|
$this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidationKnownPassword()
|
||||||
|
{
|
||||||
|
$encoder = new BCryptPasswordEncoder($this->secureRandom, self::VALID_COST);
|
||||||
|
$prefix = '$'.(version_compare(phpversion(), '5.3.7', '>=')
|
||||||
|
? '2y' : '2a').'$';
|
||||||
|
|
||||||
|
$encrypted = $prefix.'04$ABCDEFGHIJKLMNOPQRSTU.uTmwd4KMSHxbUsG7bng8x7YdA0PM1iq';
|
||||||
|
$this->assertTrue($encoder->isPasswordValid($encrypted, self::PASSWORD, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSecureRandomIsUsed()
|
||||||
|
{
|
||||||
|
if (function_exists('mcrypt_create_iv')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->secureRandom
|
||||||
|
->expects($this->atLeastOnce())
|
||||||
|
->method('nextBytes')
|
||||||
|
;
|
||||||
|
|
||||||
|
$encoder = new BCryptPasswordEncoder($this->secureRandom, self::VALID_COST);
|
||||||
|
$result = $encoder->encodePassword(self::PASSWORD, null);
|
||||||
|
|
||||||
|
$prefix = '$'.(version_compare(phpversion(), '5.3.7', '>=')
|
||||||
|
? '2y' : '2a').'$';
|
||||||
|
$salt = 'MDEyMzQ1Njc4OWFiY2RlZe';
|
||||||
|
$expected = crypt(self::PASSWORD, $prefix . self::VALID_COST . '$' . $salt);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
}
|
@ -9,10 +9,10 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Security\Tests\Core\Validator\Constraint;
|
namespace Symfony\Component\Security\Tests\Core\Validator\Constraints;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\Validator\Constraint\UserPassword;
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
|
||||||
use Symfony\Component\Security\Core\Validator\Constraint\UserPasswordValidator;
|
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator;
|
||||||
|
|
||||||
class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase
|
class UserPasswordValidatorTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
@ -82,17 +82,11 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertFalse($session->has('_security_session'));
|
$this->assertFalse($session->has('_security_session'));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function runSessionOnKernelResponse($newToken, $original = null)
|
public function testOnKernelResponseWithoutSession()
|
||||||
{
|
{
|
||||||
$session = new Session(new MockArraySessionStorage());
|
$this->securityContext->setToken(new UsernamePasswordToken('test1', 'pass1', 'phpunit'));
|
||||||
|
|
||||||
if ($original !== null) {
|
|
||||||
$session->set('_security_session', $original);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->securityContext->setToken($newToken);
|
|
||||||
|
|
||||||
$request = new Request();
|
$request = new Request();
|
||||||
|
$session = new Session(new MockArraySessionStorage());
|
||||||
$request->setSession($session);
|
$request->setSession($session);
|
||||||
|
|
||||||
$event = new FilterResponseEvent(
|
$event = new FilterResponseEvent(
|
||||||
@ -105,25 +99,7 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$listener = new ContextListener($this->securityContext, array(), 'session');
|
$listener = new ContextListener($this->securityContext, array(), 'session');
|
||||||
$listener->onKernelResponse($event);
|
$listener->onKernelResponse($event);
|
||||||
|
|
||||||
return $session;
|
$this->assertFalse($session->isStarted());
|
||||||
}
|
|
||||||
|
|
||||||
public function testOnKernelResponseWithoutSession()
|
|
||||||
{
|
|
||||||
$this->securityContext->setToken(new UsernamePasswordToken('test1', 'pass1', 'phpunit'));
|
|
||||||
$request = new Request();
|
|
||||||
|
|
||||||
$event = new FilterResponseEvent(
|
|
||||||
$this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
|
|
||||||
$request,
|
|
||||||
HttpKernelInterface::MASTER_REQUEST,
|
|
||||||
new Response()
|
|
||||||
);
|
|
||||||
|
|
||||||
$listener = new ContextListener($this->securityContext, array(), 'session');
|
|
||||||
$listener->onKernelResponse($event);
|
|
||||||
|
|
||||||
$this->assertFalse($request->hasSession());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,4 +144,30 @@ class ContextListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
array(null),
|
array(null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
protected function runSessionOnKernelResponse($newToken, $original = null)
|
||||||
|
{
|
||||||
|
$session = new Session(new MockArraySessionStorage());
|
||||||
|
|
||||||
|
if ($original !== null) {
|
||||||
|
$session->set('_security_session', $original);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->securityContext->setToken($newToken);
|
||||||
|
|
||||||
|
$request = new Request();
|
||||||
|
$request->setSession($session);
|
||||||
|
$request->cookies->set('MOCKSESSID', true);
|
||||||
|
|
||||||
|
$event = new FilterResponseEvent(
|
||||||
|
$this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'),
|
||||||
|
$request,
|
||||||
|
HttpKernelInterface::MASTER_REQUEST,
|
||||||
|
new Response()
|
||||||
|
);
|
||||||
|
|
||||||
|
$listener = new ContextListener($this->securityContext, array(), 'session');
|
||||||
|
$listener->onKernelResponse($event);
|
||||||
|
|
||||||
|
return $session;
|
||||||
|
}}
|
||||||
|
Reference in New Issue
Block a user