feature #30052 [Security] Replace serialization API (renanbr)

This PR was merged into the 4.3-dev branch.

Discussion
----------

[Security] Replace serialization API

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

New `getState()` and `setState()` methods in `AbstractToken` and `AuthenticationException` allow users to append data to the serialization payload.

It allow us to have zero impact in user land when changing the serialization engine.

Commits
-------

006c6ddda3 makes serialize methods final
This commit is contained in:
Nicolas Grekas 2019-02-07 10:19:49 +01:00
commit e8c3f9e0d0
13 changed files with 232 additions and 94 deletions

View File

@ -45,6 +45,41 @@ HttpFoundation
* The `FileinfoMimeTypeGuesser` class has been deprecated,
use `Symfony\Component\Mime\FileinfoMimeTypeGuesser` instead.
Security
--------
* The `AbstractToken::serialize()`, `AbstractToken::unserialize()`,
`AuthenticationException::serialize()` and `AuthenticationException::unserialize()`
methods are now final, use `getState()` and `setState()` instead.
Before:
```php
public function serialize()
{
return [$this->myLocalVar, parent::serialize()];
}
public function unserialize($serialized)
{
[$this->myLocalVar, $parentSerialized] = unserialize($serialized);
parent::unserialize($parentSerialized);
}
```
After:
```php
protected function getState(): array
{
return [$this->myLocalVar, parent::getState()];
}
protected function setState(array $data)
{
[$this->myLocalVar, $parentData] = $data;
parent::setState($parentData);
}
```
Yaml
----

View File

@ -232,6 +232,37 @@ Security
* `SimpleAuthenticatorInterface`, `SimpleFormAuthenticatorInterface`, `SimplePreAuthenticatorInterface`,
`SimpleAuthenticationProvider`, `SimpleAuthenticationHandler`, `SimpleFormAuthenticationListener` and
`SimplePreAuthenticationListener` have been removed. Use Guard instead.
* `\Serializable` interface has been removed from `AbstractToken` and `AuthenticationException`,
thus `serialize()` and `unserialize()` aren't available.
Use `getState()` and `setState()` instead.
Before:
```php
public function serialize()
{
return [$this->myLocalVar, parent::serialize()];
}
public function unserialize($serialized)
{
[$this->myLocalVar, $parentSerialized] = unserialize($serialized);
parent::unserialize($parentSerialized);
}
```
After:
```php
protected function getState(): array
{
return [$this->myLocalVar, parent::getState()];
}
protected function setState(array $data)
{
[$this->myLocalVar, $parentData] = $data;
parent::setState($parentData);
}
```
SecurityBundle
--------------

View File

@ -1,6 +1,12 @@
CHANGELOG
=========
4.3.0
-----
* Made the `serialize()` and `unserialize()` methods of `AbstractToken` and
`AuthenticationException` final, use `getState()`/`setState()` instead
4.2.0
-----

View File

@ -133,20 +133,67 @@ abstract class AbstractToken implements TokenInterface
/**
* {@inheritdoc}
*
* @final since Symfony 4.3, use getState() instead
*
* @internal since Symfony 4.3, use getState() instead
*/
public function serialize()
{
$serialized = [$this->user, $this->authenticated, $this->roles, $this->attributes];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return $this->doSerialize($this->getState(), \func_num_args() ? \func_get_arg(0) : null);
}
/**
* {@inheritdoc}
*
* @final since Symfony 4.3, use setState() instead
*
* @internal since Symfony 4.3, use setState() instead
*/
public function unserialize($serialized)
{
list($this->user, $this->authenticated, $this->roles, $this->attributes) = \is_array($serialized) ? $serialized : unserialize($serialized);
$this->setState(\is_array($serialized) ? $serialized : unserialize($serialized));
}
/**
* Returns all the necessary state of the object for serialization purposes.
*
* There is no need to serialize any entry, they should be returned as-is.
* If you extend this method, keep in mind you MUST guarantee parent data is present in the state.
* Here is an example of how to extend this method:
* <code>
* protected function getState(): array
* {
* return [$this->childAttribute, parent::getState()];
* }
* </code>
*
* @see setState()
*/
protected function getState(): array
{
return [$this->user, $this->authenticated, $this->roles, $this->attributes];
}
/**
* Restores the object state from an array given by getState().
*
* There is no need to unserialize any entry in $data, they are already ready-to-use.
* If you extend this method, keep in mind you MUST pass the parent data to its respective class.
* Here is an example of how to extend this method:
* <code>
* protected function setState(array $data)
* {
* [$this->childAttribute, $parentData] = $data;
* parent::setState($parentData);
* }
* </code>
*
* @see getState()
*/
protected function setState(array $data)
{
[$this->user, $this->authenticated, $this->roles, $this->attributes] = $data;
}
/**

View File

@ -57,19 +57,17 @@ class AnonymousToken extends AbstractToken
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [$this->secret, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [$this->secret, parent::getState()];
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
protected function setState(array $data)
{
list($this->secret, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
parent::unserialize($parentStr);
[$this->secret, $parentData] = $data;
parent::setState($parentData);
}
}

View File

@ -77,19 +77,17 @@ class PreAuthenticatedToken extends AbstractToken
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [$this->credentials, $this->providerKey, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [$this->credentials, $this->providerKey, parent::getState()];
}
/**
* {@inheritdoc}
*/
public function unserialize($str)
protected function setState(array $data)
{
list($this->credentials, $this->providerKey, $parentStr) = \is_array($str) ? $str : unserialize($str);
parent::unserialize($parentStr);
[$this->credentials, $this->providerKey, $parentData] = $data;
parent::setState($parentData);
}
}

View File

@ -92,19 +92,17 @@ class RememberMeToken extends AbstractToken
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [$this->secret, $this->providerKey, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [$this->secret, $this->providerKey, parent::getState()];
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
protected function setState(array $data)
{
list($this->secret, $this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
parent::unserialize($parentStr);
[$this->secret, $this->providerKey, $parentData] = $data;
parent::setState($parentData);
}
}

View File

@ -89,19 +89,17 @@ class UsernamePasswordToken extends AbstractToken
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [$this->credentials, $this->providerKey, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [$this->credentials, $this->providerKey, parent::getState()];
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
protected function setState(array $data)
{
list($this->credentials, $this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
parent::unserialize($parentStr);
[$this->credentials, $this->providerKey, $parentData] = $data;
parent::setState($parentData);
}
}

View File

@ -42,20 +42,17 @@ abstract class AccountStatusException extends AuthenticationException
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [$this->user, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [$this->user, parent::getState()];
}
/**
* {@inheritdoc}
*/
public function unserialize($str)
protected function setState(array $data)
{
list($this->user, $parentData) = \is_array($str) ? $str : unserialize($str);
parent::unserialize($parentData);
[$this->user, $parentData] = $data;
parent::setState($parentData);
}
}

View File

@ -40,18 +40,67 @@ class AuthenticationException extends RuntimeException implements \Serializable
/**
* {@inheritdoc}
*
* @final since Symfony 4.3, use getState() instead
*
* @internal since Symfony 4.3, use getState() instead
*/
public function serialize()
{
$serialized = [
$this->token,
$this->code,
$this->message,
$this->file,
$this->line,
];
return $this->doSerialize($this->getState(), \func_num_args() ? \func_get_arg(0) : null);
}
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
/**
* {@inheritdoc}
*
* @final since Symfony 4.3, use setState() instead
*
* @internal since Symfony 4.3, use setState() instead
*/
public function unserialize($serialized)
{
$this->setState(\is_array($serialized) ? $serialized : unserialize($serialized));
}
/**
* Returns all the necessary state of the object for serialization purposes.
*
* There is no need to serialize any entry, they should be returned as-is.
* If you extend this method, keep in mind you MUST guarantee parent data is present in the state.
* Here is an example of how to extend this method:
* <code>
* protected function getState(): array
* {
* return [$this->childAttribute, parent::getState()];
* }
* </code>
*
* @see setState()
*/
protected function getState(): array
{
return [$this->token, $this->code, $this->message, $this->file, $this->line];
}
/**
* Restores the object state from an array given by getState().
*
* There is no need to unserialize any entry in $data, they are already ready-to-use.
* If you extend this method, keep in mind you MUST pass the parent data to its respective class.
* Here is an example of how to extend this method:
* <code>
* protected function setState(array $data)
* {
* [$this->childAttribute, $parentData] = $data;
* parent::setState($parentData);
* }
* </code>
*
* @see getState()
*/
protected function setState(array $data)
{
[$this->token, $this->code, $this->message, $this->file, $this->line] = $data;
}
/**
@ -67,17 +116,6 @@ class AuthenticationException extends RuntimeException implements \Serializable
return $isCalledFromOverridingMethod ? $serialized : serialize($serialized);
}
public function unserialize($str)
{
list(
$this->token,
$this->code,
$this->message,
$this->file,
$this->line
) = \is_array($str) ? $str : unserialize($str);
}
/**
* Message key to be used by the translation component.
*

View File

@ -58,20 +58,17 @@ class CustomUserMessageAuthenticationException extends AuthenticationException
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [parent::serialize(true), $this->messageKey, $this->messageData];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [parent::getState(), $this->messageKey, $this->messageData];
}
/**
* {@inheritdoc}
*/
public function unserialize($str)
protected function setState(array $data)
{
list($parentData, $this->messageKey, $this->messageData) = \is_array($str) ? $str : unserialize($str);
parent::unserialize($parentData);
[$parentData, $this->messageKey, $this->messageData] = $data;
parent::setState($parentData);
}
}

View File

@ -49,26 +49,6 @@ class UsernameNotFoundException extends AuthenticationException
$this->username = $username;
}
/**
* {@inheritdoc}
*/
public function serialize()
{
$serialized = [$this->username, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
}
/**
* {@inheritdoc}
*/
public function unserialize($str)
{
list($this->username, $parentData) = \is_array($str) ? $str : unserialize($str);
parent::unserialize($parentData);
}
/**
* {@inheritdoc}
*/
@ -76,4 +56,21 @@ class UsernameNotFoundException extends AuthenticationException
{
return ['{{ username }}' => $this->username];
}
/**
* {@inheritdoc}
*/
protected function getState(): array
{
return [$this->username, parent::getState()];
}
/**
* {@inheritdoc}
*/
protected function setState(array $data)
{
[$this->username, $parentData] = $data;
parent::setState($parentData);
}
}

View File

@ -74,19 +74,17 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
/**
* {@inheritdoc}
*/
public function serialize()
protected function getState(): array
{
$serialized = [$this->providerKey, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
return [$this->providerKey, parent::getState()];
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
protected function setState(array $data)
{
list($this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
parent::unserialize($parentStr);
[$this->providerKey, $parentData] = $data;
parent::setState($parentData);
}
}