From 9cb7087e70114f840bc1e7f0885589a64cb31994 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 19 May 2021 15:18:37 +0200 Subject: [PATCH 001/138] Bump Symfony 6 to PHP 8 --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d129ffee..22ed7990 100644 --- a/composer.json +++ b/composer.json @@ -16,9 +16,8 @@ } ], "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "symfony/event-dispatcher-contracts": "^1.1|^2", - "symfony/polyfill-php80": "^1.15", "symfony/service-contracts": "^1.1.6|^2", "symfony/deprecation-contracts": "^2.1", "symfony/password-hasher": "^5.3" From 7b5aa72a3ef757e2688263b43b861e9846ef91d7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 20 May 2021 14:59:02 +0200 Subject: [PATCH 002/138] Bump symfony/* deps to ^5.4|^6.0 --- composer.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 0d83ee4d..9cb24782 100644 --- a/composer.json +++ b/composer.json @@ -20,26 +20,26 @@ "symfony/event-dispatcher-contracts": "^1.1|^2", "symfony/service-contracts": "^1.1.6|^2", "symfony/deprecation-contracts": "^2.1", - "symfony/password-hasher": "^5.3|^6.0" + "symfony/password-hasher": "^5.4|^6.0" }, "require-dev": { "psr/container": "^1.0|^2.0", "psr/cache": "^1.0|^2.0|^3.0", - "symfony/cache": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^5.3|^6.0", - "symfony/ldap": "^4.4|^5.0|^6.0", - "symfony/translation": "^4.4|^5.0|^6.0", - "symfony/validator": "^5.2|^6.0", + "symfony/cache": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/ldap": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0", "psr/log": "~1.0" }, "conflict": { - "symfony/event-dispatcher": "<4.4", - "symfony/http-foundation": "<5.3", - "symfony/security-guard": "<4.4", - "symfony/ldap": "<4.4", - "symfony/validator": "<5.2" + "symfony/event-dispatcher": "<5.4", + "symfony/http-foundation": "<5.4", + "symfony/security-guard": "<5.4", + "symfony/ldap": "<5.4", + "symfony/validator": "<5.4" }, "suggest": { "psr/container-implementation": "To instantiate the Security class", From 4ddff07122232230605d5ed5a097f0249ebd0587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 19 May 2021 20:52:47 +0200 Subject: [PATCH 003/138] Remove constraint for PHP < 8 --- Authorization/Voter/Voter.php | 7 +------ Tests/Validator/Constraints/UserPasswordTest.php | 9 +-------- .../Validator/Constraints/UserPasswordValidatorTest.php | 4 +--- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/Authorization/Voter/Voter.php b/Authorization/Voter/Voter.php index f6e853a6..49ec770e 100644 --- a/Authorization/Voter/Voter.php +++ b/Authorization/Voter/Voter.php @@ -35,12 +35,7 @@ public function vote(TokenInterface $token, $subject, array $attributes) continue; } } catch (\TypeError $e) { - if (\PHP_VERSION_ID < 80000) { - if (0 === strpos($e->getMessage(), 'Argument 1 passed to') - && false !== strpos($e->getMessage(), '::supports() must be of the type string')) { - continue; - } - } elseif (false !== strpos($e->getMessage(), 'supports(): Argument #1')) { + if (false !== strpos($e->getMessage(), 'supports(): Argument #1')) { continue; } diff --git a/Tests/Validator/Constraints/UserPasswordTest.php b/Tests/Validator/Constraints/UserPasswordTest.php index 8e956c38..49a836db 100644 --- a/Tests/Validator/Constraints/UserPasswordTest.php +++ b/Tests/Validator/Constraints/UserPasswordTest.php @@ -37,11 +37,7 @@ public function provideServiceValidatedConstraints(): iterable { yield 'Doctrine style' => [new UserPassword(['service' => 'my_service'])]; - if (\PHP_VERSION_ID < 80000) { - return; - } - - yield 'named arguments' => [eval('return new \Symfony\Component\Security\Core\Validator\Constraints\UserPassword(service: "my_service");')]; + yield 'named arguments' => [new UserPassword(service: "my_service")]; $metadata = new ClassMetadata(UserPasswordDummy::class); self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata)); @@ -49,9 +45,6 @@ public function provideServiceValidatedConstraints(): iterable yield 'attribute' => [$metadata->properties['b']->constraints[0]]; } - /** - * @requires PHP 8 - */ public function testAttributes() { $metadata = new ClassMetadata(UserPasswordDummy::class); diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 2be5a50d..5e731ae0 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -95,9 +95,7 @@ public function provideConstraints(): iterable { yield 'Doctrine style' => [new UserPassword(['message' => 'myMessage'])]; - if (\PHP_VERSION_ID >= 80000) { - yield 'named arguments' => [eval('return new \Symfony\Component\Security\Core\Validator\Constraints\UserPassword(message: "myMessage");')]; - } + yield 'named arguments' => [new UserPassword(message: "myMessage")]; } /** From 11c3f2840937cb282036265facc3882bb6bec711 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 May 2021 00:47:05 +0200 Subject: [PATCH 004/138] Remove Serializable implementations --- Authentication/Token/AbstractToken.php | 4 ++-- Authentication/Token/NullToken.php | 21 ------------------ Authentication/Token/TokenInterface.php | 2 +- CHANGELOG.md | 5 +++++ .../Token/AbstractTokenTest.php | 12 +--------- .../Token/Fixtures/switch-user-token-4.4.txt | Bin 1319 -> 1302 bytes .../Token/SwitchUserTokenTest.php | 2 +- 7 files changed, 10 insertions(+), 36 deletions(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index a68a27d4..709591e2 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -21,7 +21,7 @@ * @author Fabien Potencier * @author Johannes M. Schmitt */ -abstract class AbstractToken implements TokenInterface +abstract class AbstractToken implements TokenInterface, \Serializable { private $user; private $roleNames = []; @@ -264,7 +264,7 @@ public function __toString() */ final public function serialize(): string { - return serialize($this->__serialize()); + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } /** diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index 28c77d75..eb0e74df 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -100,25 +100,4 @@ public function __serialize(): array public function __unserialize(array $data): void { } - - /** - * @return string - * - * @internal in 5.3 - * @final in 5.3 - */ - public function serialize() - { - return ''; - } - - /** - * @return void - * - * @internal in 5.3 - * @final in 5.3 - */ - public function unserialize($serialized) - { - } } diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index 047f571a..743bb4f1 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -21,7 +21,7 @@ * @author Fabien Potencier * @author Johannes M. Schmitt */ -interface TokenInterface extends \Serializable +interface TokenInterface { /** * Returns a string representation of the Token. diff --git a/CHANGELOG.md b/CHANGELOG.md index 22652b08..c60960c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.0 +--- + + * `TokenInterface` does not extend `Serializable` anymore + 5.3 --- diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index dcf479c8..cf80f459 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -232,7 +232,7 @@ public function __toString(): string } } -class SerializableUser implements UserInterface, \Serializable +class SerializableUser implements UserInterface { private $roles; private $name; @@ -275,16 +275,6 @@ public function getSalt() { return null; } - - public function serialize(): string - { - return serialize($this->name); - } - - public function unserialize($serialized): void - { - $this->name = unserialize($serialized); - } } class ConcreteToken extends AbstractToken diff --git a/Tests/Authentication/Token/Fixtures/switch-user-token-4.4.txt b/Tests/Authentication/Token/Fixtures/switch-user-token-4.4.txt index fc8af1432871f11d7804a00d6f09fe38f8a6e582..5103e3db29fd979a4bf8a9c9b36d179d6499a943 100644 GIT binary patch delta 45 zcmZ3^HI0kWe!Uzm>VkWJ6}9iE5$~r-@Jg!1QPH21ZN9&99hjm;f+Y B4q^ZR delta 58 zcmbQnwVaF5c_O2?u%VH$nN@Y7m626-rj>!Uvz58YWJPABiIqC6re@|=)sycr{oA~d O(UNiVO(t6=###W{I1x1f diff --git a/Tests/Authentication/Token/SwitchUserTokenTest.php b/Tests/Authentication/Token/SwitchUserTokenTest.php index e605615b..90b80adc 100644 --- a/Tests/Authentication/Token/SwitchUserTokenTest.php +++ b/Tests/Authentication/Token/SwitchUserTokenTest.php @@ -94,7 +94,7 @@ public function testSerializeNullImpersonateUrl() /** * Tests if an old version of SwitchUserToken can still be unserialized. * - * The fixture was generated by running the following code with Symfony 4.4 and PHP 7.2. + * The fixture was generated by running the following code with Symfony 4.4 and PHP 8.0. * * serialize( * new SwitchUserToken( From ca8f29f722b29cb4abd2803f251f37f2fb90a5a2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 May 2021 09:59:09 +0200 Subject: [PATCH 005/138] Leverage Stringable --- Authentication/Token/AbstractToken.php | 2 +- Signature/SignatureHasher.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 709591e2..0b8389e9 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -95,7 +95,7 @@ public function getUser() */ public function setUser($user) { - if (!($user instanceof UserInterface || (\is_object($user) && method_exists($user, '__toString')) || \is_string($user))) { + if (!($user instanceof UserInterface || $user instanceof \Stringable || \is_string($user))) { throw new \InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.'); } diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index ad402832..93d5358f 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -88,7 +88,7 @@ public function computeSignatureHash(UserInterface $user, int $expires): string $value = $value->format('c'); } - if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { + if (!is_scalar($value) && !$value instanceof \Stringable) { throw new \InvalidArgumentException(sprintf('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned.', $property, \get_class($user), get_debug_type($value))); } $signatureFields[] = base64_encode($value); From 7252399456a0dcd5100fe0d267c198987116c379 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 May 2021 17:52:26 +0200 Subject: [PATCH 006/138] Add return type to __toString() --- Authentication/Token/AbstractToken.php | 5 +---- Authentication/Token/TokenInterface.php | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 0b8389e9..6e5dd44f 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -243,10 +243,7 @@ public function setAttribute(string $name, $value) $this->attributes[$name] = $value; } - /** - * {@inheritdoc} - */ - public function __toString() + public function __toString(): string { $class = static::class; $class = substr($class, strrpos($class, '\\') + 1); diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index 743bb4f1..9480bdc7 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -27,10 +27,8 @@ interface TokenInterface * Returns a string representation of the Token. * * This is only to be used for debugging purposes. - * - * @return string */ - public function __toString(); + public function __toString(): string; /** * Returns the user roles. From 0d9006941f0c5d630d89f0b96a5fc174d5639624 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 2 Jun 2021 18:09:43 +0200 Subject: [PATCH 007/138] Update phpunit.xml.dist files for phpunit >= 9.3 --- phpunit.xml.dist | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5b56c184..223091f3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ - - + + ./ - - ./Resources - ./Tests - ./vendor - - - + + + ./Resources + ./Tests + ./vendor + + From b5fa562d577a6d9df96424cceac558ee4ad7395d Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 21 Jun 2021 11:50:22 +0200 Subject: [PATCH 008/138] Remove code for old libxml versions Signed-off-by: Alexander M. Turek --- Tests/Resources/TranslationFilesTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index c0607ed1..cd96e7e6 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -36,9 +36,6 @@ public function testTranslationFileIsValidWithoutEntityLoader($filePath) { $document = new \DOMDocument(); $document->loadXML(file_get_contents($filePath)); - if (\LIBXML_VERSION < 20900) { - libxml_disable_entity_loader(true); - } $errors = XliffUtils::validateSchema($document); From bdecbe1a770bb30eb77b2a61f3e685e15fcb54cd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 30 Jun 2021 09:34:09 +0200 Subject: [PATCH 009/138] [Security] add union types --- Authentication/Token/AbstractToken.php | 17 ++++------------- Authentication/Token/AnonymousToken.php | 7 +++---- Authentication/Token/NullToken.php | 6 ++++-- Authentication/Token/PreAuthenticatedToken.php | 6 ++---- Authentication/Token/SwitchUserToken.php | 10 ++++++---- Authentication/Token/TokenInterface.php | 8 ++------ Authentication/Token/UsernamePasswordToken.php | 9 +-------- Authorization/AccessDecisionManager.php | 12 +++++------- .../AccessDecisionManagerInterface.php | 2 +- Authorization/AuthorizationChecker.php | 2 +- Authorization/AuthorizationCheckerInterface.php | 3 +-- .../TraceableAccessDecisionManager.php | 6 ++---- Authorization/Voter/AuthenticatedVoter.php | 2 +- Authorization/Voter/ExpressionVoter.php | 4 ++-- Authorization/Voter/RoleVoter.php | 2 +- Authorization/Voter/TraceableVoter.php | 2 +- Authorization/Voter/Voter.php | 11 ++++------- Authorization/Voter/VoterInterface.php | 2 +- Event/VoteEvent.php | 4 ++-- Exception/AccessDeniedException.php | 10 ++-------- Security.php | 5 +---- User/ChainUserProvider.php | 8 +------- Validator/Constraints/UserPassword.php | 2 +- Validator/Constraints/UserPasswordValidator.php | 15 ++++++--------- 24 files changed, 55 insertions(+), 100 deletions(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 0316634a..507e0d48 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -93,12 +93,8 @@ public function getUser() /** * {@inheritdoc} */ - public function setUser($user) + public function setUser(string|\Stringable|UserInterface $user) { - if (!($user instanceof UserInterface || $user instanceof \Stringable || \is_string($user))) { - throw new \InvalidArgumentException('$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.'); - } - if (null === $this->user) { $changed = false; } elseif ($this->user instanceof UserInterface) { @@ -233,12 +229,7 @@ public function getAttribute(string $name) return $this->attributes[$name]; } - /** - * Sets an attribute. - * - * @param mixed $value The attribute value - */ - public function setAttribute(string $name, $value) + public function setAttribute(string $name, mixed $value) { $this->attributes[$name] = $value; } @@ -267,9 +258,9 @@ final public function serialize(): string /** * @internal */ - final public function unserialize($serialized) + final public function unserialize(string $serialized) { - $this->__unserialize(\is_array($serialized) ? $serialized : unserialize($serialized)); + $this->__unserialize(unserialize($serialized)); } private function hasUserChanged(UserInterface $user): bool diff --git a/Authentication/Token/AnonymousToken.php b/Authentication/Token/AnonymousToken.php index db94766d..4f3c7438 100644 --- a/Authentication/Token/AnonymousToken.php +++ b/Authentication/Token/AnonymousToken.php @@ -23,11 +23,10 @@ class AnonymousToken extends AbstractToken private $secret; /** - * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client - * @param string|\Stringable|UserInterface $user - * @param string[] $roles + * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client + * @param string[] $roles */ - public function __construct(string $secret, $user, array $roles = []) + public function __construct(string $secret, string|\Stringable|UserInterface $user, array $roles = []) { parent::__construct($roles); diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index eb0e74df..a046ea4a 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Authentication\Token; +use Symfony\Component\Security\Core\User\UserInterface; + /** * @author Wouter de Jong */ @@ -36,7 +38,7 @@ public function getUser() return ''; } - public function setUser($user) + public function setUser(string|\Stringable|UserInterface $user) { throw new \BadMethodCallException('Cannot set user on a NullToken.'); } @@ -87,7 +89,7 @@ public function getAttribute(string $name) return null; } - public function setAttribute(string $name, $value) + public function setAttribute(string $name, mixed $value) { throw new \BadMethodCallException('Cannot add attribute to NullToken.'); } diff --git a/Authentication/Token/PreAuthenticatedToken.php b/Authentication/Token/PreAuthenticatedToken.php index 95a4d2d7..6ddee969 100644 --- a/Authentication/Token/PreAuthenticatedToken.php +++ b/Authentication/Token/PreAuthenticatedToken.php @@ -24,11 +24,9 @@ class PreAuthenticatedToken extends AbstractToken private $firewallName; /** - * @param string|\Stringable|UserInterface $user - * @param mixed $credentials - * @param string[] $roles + * @param string[] $roles */ - public function __construct($user, $credentials, string $firewallName, array $roles = []) + public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, string $firewallName, array $roles = []) { parent::__construct($roles); diff --git a/Authentication/Token/SwitchUserToken.php b/Authentication/Token/SwitchUserToken.php index ccccb5b5..a6e96c2a 100644 --- a/Authentication/Token/SwitchUserToken.php +++ b/Authentication/Token/SwitchUserToken.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Authentication\Token; +use Symfony\Component\Security\Core\User\UserInterface; + /** * Token representing a user who temporarily impersonates another one. * @@ -22,13 +24,13 @@ class SwitchUserToken extends UsernamePasswordToken private $originatedFromUri; /** - * @param string|object $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method - * @param mixed $credentials This usually is the password of the user - * @param string|null $originatedFromUri The URI where was the user at the switch + * @param $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method + * @param $credentials This usually is the password of the user + * @param $originatedFromUri The URI where was the user at the switch * * @throws \InvalidArgumentException */ - public function __construct($user, $credentials, string $firewallName, array $roles, TokenInterface $originalToken, string $originatedFromUri = null) + public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, string $firewallName, array $roles, TokenInterface $originalToken, string $originatedFromUri = null) { parent::__construct($user, $credentials, $firewallName, $roles); diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index 83471244..e6ab18d6 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -59,11 +59,9 @@ public function getUser(); * The user can be a UserInterface instance, or an object implementing * a __toString method or the username as a regular string. * - * @param string|\Stringable|UserInterface $user - * * @throws \InvalidArgumentException */ - public function setUser($user); + public function setUser(string|\Stringable|UserInterface $user); /** * Returns whether the user is authenticated or not. @@ -114,10 +112,8 @@ public function getAttribute(string $name); /** * Sets an attribute. - * - * @param mixed $value The attribute value */ - public function setAttribute(string $name, $value); + public function setAttribute(string $name, mixed $value); /** * Returns all the necessary state of the object for serialization purposes. diff --git a/Authentication/Token/UsernamePasswordToken.php b/Authentication/Token/UsernamePasswordToken.php index 8228f677..9af22161 100644 --- a/Authentication/Token/UsernamePasswordToken.php +++ b/Authentication/Token/UsernamePasswordToken.php @@ -23,14 +23,7 @@ class UsernamePasswordToken extends AbstractToken private $credentials; private $firewallName; - /** - * @param string|\Stringable|UserInterface $user The username (like a nickname, email address, etc.) or a UserInterface instance - * @param mixed $credentials - * @param string[] $roles - * - * @throws \InvalidArgumentException - */ - public function __construct($user, $credentials, string $firewallName, array $roles = []) + public function __construct(string|\Stringable|UserInterface $user, mixed $credentials, string $firewallName, array $roles = []) { parent::__construct($roles); diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index 82f9e0ae..8da482d3 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -59,10 +59,8 @@ public function __construct(iterable $voters = [], string $strategy = self::STRA * * {@inheritdoc} */ - public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/) + public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false) { - $allowMultipleAttributes = 3 < \func_num_args() && func_get_arg(3); - // Special case for AccessListener, do not remove the right side of the condition before 6.0 if (\count($attributes) > 1 && !$allowMultipleAttributes) { throw new InvalidArgumentException(sprintf('Passing more than one Security attribute to "%s()" is not supported.', __METHOD__)); @@ -77,7 +75,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null/ * If all voters abstained from voting, the decision will be based on the * allowIfAllAbstainDecisions property value (defaults to false). */ - private function decideAffirmative(TokenInterface $token, array $attributes, $object = null): bool + private function decideAffirmative(TokenInterface $token, array $attributes, mixed $object = null): bool { $deny = 0; foreach ($this->voters as $voter) { @@ -115,7 +113,7 @@ private function decideAffirmative(TokenInterface $token, array $attributes, $ob * If all voters abstained from voting, the decision will be based on the * allowIfAllAbstainDecisions property value (defaults to false). */ - private function decideConsensus(TokenInterface $token, array $attributes, $object = null): bool + private function decideConsensus(TokenInterface $token, array $attributes, mixed $object = null): bool { $grant = 0; $deny = 0; @@ -152,7 +150,7 @@ private function decideConsensus(TokenInterface $token, array $attributes, $obje * If all voters abstained from voting, the decision will be based on the * allowIfAllAbstainDecisions property value (defaults to false). */ - private function decideUnanimous(TokenInterface $token, array $attributes, $object = null): bool + private function decideUnanimous(TokenInterface $token, array $attributes, mixed $object = null): bool { $grant = 0; foreach ($this->voters as $voter) { @@ -186,7 +184,7 @@ private function decideUnanimous(TokenInterface $token, array $attributes, $obje * If all voters abstained from voting, the decision will be based on the * allowIfAllAbstainDecisions property value (defaults to false). */ - private function decidePriority(TokenInterface $token, array $attributes, $object = null) + private function decidePriority(TokenInterface $token, array $attributes, mixed $object = null) { foreach ($this->voters as $voter) { $result = $voter->vote($token, $object, $attributes); diff --git a/Authorization/AccessDecisionManagerInterface.php b/Authorization/AccessDecisionManagerInterface.php index 7a2ebc45..ae34d1e3 100644 --- a/Authorization/AccessDecisionManagerInterface.php +++ b/Authorization/AccessDecisionManagerInterface.php @@ -28,5 +28,5 @@ interface AccessDecisionManagerInterface * * @return bool true if the access is granted, false otherwise */ - public function decide(TokenInterface $token, array $attributes, $object = null); + public function decide(TokenInterface $token, array $attributes, mixed $object = null); } diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index c51551a0..0103bb0c 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -46,7 +46,7 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM * * @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token and $exceptionOnNoToken is set to true */ - final public function isGranted($attribute, $subject = null): bool + final public function isGranted(mixed $attribute, mixed $subject = null): bool { if (null === ($token = $this->tokenStorage->getToken())) { if ($this->exceptionOnNoToken) { diff --git a/Authorization/AuthorizationCheckerInterface.php b/Authorization/AuthorizationCheckerInterface.php index f60c80b7..97e53cb8 100644 --- a/Authorization/AuthorizationCheckerInterface.php +++ b/Authorization/AuthorizationCheckerInterface.php @@ -22,9 +22,8 @@ interface AuthorizationCheckerInterface * Checks if the attribute is granted against the current authentication token and optionally supplied subject. * * @param mixed $attribute A single attribute to vote on (can be of any type, string and instance of Expression are supported by the core) - * @param mixed $subject * * @return bool */ - public function isGranted($attribute, $subject = null); + public function isGranted(mixed $attribute, mixed $subject = null); } diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 3b5004ed..1f997d05 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -47,10 +47,8 @@ public function __construct(AccessDecisionManagerInterface $manager) /** * {@inheritdoc} - * - * @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array */ - public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/): bool + public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false): bool { $currentDecisionLog = [ 'attributes' => $attributes, @@ -60,7 +58,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null/ $this->currentLog[] = &$currentDecisionLog; - $result = $this->manager->decide($token, $attributes, $object, 3 < \func_num_args() && func_get_arg(3)); + $result = $this->manager->decide($token, $attributes, $object, $allowMultipleAttributes); $currentDecisionLog['result'] = $result; diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index fd6a65f2..b16d88f2 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -44,7 +44,7 @@ public function __construct(AuthenticationTrustResolverInterface $authentication /** * {@inheritdoc} */ - public function vote(TokenInterface $token, $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes) { if ($attributes === [self::PUBLIC_ACCESS]) { return VoterInterface::ACCESS_GRANTED; diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index f02c4246..1db0f875 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -42,7 +42,7 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati /** * {@inheritdoc} */ - public function vote(TokenInterface $token, $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes) { $result = VoterInterface::ACCESS_ABSTAIN; $variables = null; @@ -64,7 +64,7 @@ public function vote(TokenInterface $token, $subject, array $attributes) return $result; } - private function getVariables(TokenInterface $token, $subject): array + private function getVariables(TokenInterface $token, mixed $subject): array { $roleNames = $token->getRoleNames(); diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index cd5a243b..3060922b 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -30,7 +30,7 @@ public function __construct(string $prefix = 'ROLE_') /** * {@inheritdoc} */ - public function vote(TokenInterface $token, $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes) { $result = VoterInterface::ACCESS_ABSTAIN; $roles = $this->extractRoles($token); diff --git a/Authorization/Voter/TraceableVoter.php b/Authorization/Voter/TraceableVoter.php index bdbdb84b..c1f23ed2 100644 --- a/Authorization/Voter/TraceableVoter.php +++ b/Authorization/Voter/TraceableVoter.php @@ -33,7 +33,7 @@ public function __construct(VoterInterface $voter, EventDispatcherInterface $eve $this->eventDispatcher = $eventDispatcher; } - public function vote(TokenInterface $token, $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes) { $result = $this->voter->vote($token, $subject, $attributes); diff --git a/Authorization/Voter/Voter.php b/Authorization/Voter/Voter.php index 49ec770e..748a4da2 100644 --- a/Authorization/Voter/Voter.php +++ b/Authorization/Voter/Voter.php @@ -24,7 +24,7 @@ abstract class Voter implements VoterInterface /** * {@inheritdoc} */ - public function vote(TokenInterface $token, $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes) { // abstain vote by default in case none of the attributes are supported $vote = self::ACCESS_ABSTAIN; @@ -57,20 +57,17 @@ public function vote(TokenInterface $token, $subject, array $attributes) /** * Determines if the attribute and subject are supported by this voter. * - * @param string $attribute An attribute - * @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type + * @param $subject The subject to secure, e.g. an object the user wants to access or any other PHP type * * @return bool True if the attribute and subject are supported, false otherwise */ - abstract protected function supports(string $attribute, $subject); + abstract protected function supports(string $attribute, mixed $subject); /** * Perform a single access check operation on a given attribute, subject and token. * It is safe to assume that $attribute and $subject already passed the "supports()" method check. * - * @param mixed $subject - * * @return bool */ - abstract protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token); + abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token); } diff --git a/Authorization/Voter/VoterInterface.php b/Authorization/Voter/VoterInterface.php index a50af88e..7e401c3f 100644 --- a/Authorization/Voter/VoterInterface.php +++ b/Authorization/Voter/VoterInterface.php @@ -35,5 +35,5 @@ interface VoterInterface * * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED */ - public function vote(TokenInterface $token, $subject, array $attributes); + public function vote(TokenInterface $token, mixed $subject, array $attributes); } diff --git a/Event/VoteEvent.php b/Event/VoteEvent.php index 78ac2f90..ef2756fc 100644 --- a/Event/VoteEvent.php +++ b/Event/VoteEvent.php @@ -28,7 +28,7 @@ final class VoteEvent extends Event private $attributes; private $vote; - public function __construct(VoterInterface $voter, $subject, array $attributes, int $vote) + public function __construct(VoterInterface $voter, mixed $subject, array $attributes, int $vote) { $this->voter = $voter; $this->subject = $subject; @@ -41,7 +41,7 @@ public function getVoter(): VoterInterface return $this->voter; } - public function getSubject() + public function getSubject(): mixed { return $this->subject; } diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index 0e59dc40..2eb38e79 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -34,10 +34,7 @@ public function getAttributes() return $this->attributes; } - /** - * @param array|string $attributes - */ - public function setAttributes($attributes) + public function setAttributes(array|string $attributes) { $this->attributes = (array) $attributes; } @@ -50,10 +47,7 @@ public function getSubject() return $this->subject; } - /** - * @param mixed $subject - */ - public function setSubject($subject) + public function setSubject(mixed $subject) { $this->subject = $subject; } diff --git a/Security.php b/Security.php index 72e87ea4..0703546d 100644 --- a/Security.php +++ b/Security.php @@ -55,11 +55,8 @@ public function getUser(): ?UserInterface /** * Checks if the attributes are granted against the current authentication token and optionally supplied subject. - * - * @param mixed $attributes - * @param mixed $subject */ - public function isGranted($attributes, $subject = null): bool + public function isGranted(mixed $attributes, mixed $subject = null): bool { return $this->container->get('security.authorization_checker') ->isGranted($attributes, $subject); diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index a0931460..fb658684 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -126,16 +126,10 @@ public function supportsClass(string $class) } /** - * @param PasswordAuthenticatedUserInterface $user - * * {@inheritdoc} */ - public function upgradePassword($user, string $newEncodedPassword): void + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newEncodedPassword): void { - if (!$user instanceof PasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'The "%s::upgradePassword()" method expects an instance of "%s" as first argument, the "%s" class should implement it.', PasswordUpgraderInterface::class, PasswordAuthenticatedUserInterface::class, get_debug_type($user)); - } - foreach ($this->providers as $provider) { if ($provider instanceof PasswordUpgraderInterface) { try { diff --git a/Validator/Constraints/UserPassword.php b/Validator/Constraints/UserPassword.php index f9de2139..7094ca98 100644 --- a/Validator/Constraints/UserPassword.php +++ b/Validator/Constraints/UserPassword.php @@ -23,7 +23,7 @@ class UserPassword extends Constraint public $message = 'This value should be the user\'s current password.'; public $service = 'security.validator.user_password'; - public function __construct(array $options = null, string $message = null, string $service = null, array $groups = null, $payload = null) + public function __construct(array $options = null, string $message = null, string $service = null, array $groups = null, mixed $payload = null) { parent::__construct($options, $groups, $payload); diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index bf273f2f..7fb653c4 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -28,15 +28,8 @@ class UserPasswordValidator extends ConstraintValidator private $tokenStorage; private $hasherFactory; - /** - * @param PasswordHasherFactoryInterface $hasherFactory - */ - public function __construct(TokenStorageInterface $tokenStorage, $hasherFactory) + public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherFactoryInterface $hasherFactory) { - if ($hasherFactory instanceof EncoderFactoryInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'Passing a "%s" instance to the "%s" constructor is deprecated, use "%s" instead.', EncoderFactoryInterface::class, __CLASS__, PasswordHasherFactoryInterface::class); - } - $this->tokenStorage = $tokenStorage; $this->hasherFactory = $hasherFactory; } @@ -44,7 +37,7 @@ public function __construct(TokenStorageInterface $tokenStorage, $hasherFactory) /** * {@inheritdoc} */ - public function validate($password, Constraint $constraint) + public function validate(mixed $password, Constraint $constraint) { if (!$constraint instanceof UserPassword) { throw new UnexpectedTypeException($constraint, UserPassword::class); @@ -56,6 +49,10 @@ public function validate($password, Constraint $constraint) return; } + if (!\is_string($password)) { + throw new UnexpectedTypeException($password, 'string'); + } + $user = $this->tokenStorage->getToken()->getUser(); if (!$user instanceof UserInterface) { From 92699e1b37dc1310bbeb331915939eea841c5040 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Sat, 3 Jul 2021 17:11:13 +0200 Subject: [PATCH 010/138] [Security][PasswordHasher] Remove legacy password encoders --- .../Provider/DaoAuthenticationProvider.php | 25 +- CHANGELOG.md | 1 + Encoder/BasePasswordEncoder.php | 102 ------- Encoder/EncoderAwareInterface.php | 32 --- Encoder/EncoderFactory.php | 227 --------------- Encoder/EncoderFactoryInterface.php | 38 --- Encoder/LegacyEncoderTrait.php | 56 ---- Encoder/LegacyPasswordHasherEncoder.php | 52 ---- Encoder/MessageDigestPasswordEncoder.php | 87 ------ Encoder/MigratingPasswordEncoder.php | 77 ----- Encoder/NativePasswordEncoder.php | 38 --- Encoder/PasswordEncoderInterface.php | 55 ---- Encoder/PasswordHasherAdapter.php | 46 --- Encoder/PasswordHasherEncoder.php | 60 ---- Encoder/Pbkdf2PasswordEncoder.php | 47 --- Encoder/PlaintextPasswordEncoder.php | 38 --- Encoder/SelfSaltingEncoderInterface.php | 28 -- Encoder/SodiumPasswordEncoder.php | 40 --- Encoder/UserPasswordEncoder.php | 83 ------ Encoder/UserPasswordEncoderInterface.php | 44 --- .../DaoAuthenticationProviderTest.php | 13 - Tests/Encoder/EncoderFactoryTest.php | 272 ------------------ .../MyMessageDigestPasswordEncoder.php | 34 --- .../MessageDigestPasswordEncoderTest.php | 73 ----- .../Encoder/MigratingPasswordEncoderTest.php | 70 ----- Tests/Encoder/NativePasswordEncoderTest.php | 106 ------- Tests/Encoder/Pbkdf2PasswordEncoderTest.php | 63 ---- .../Encoder/PlaintextPasswordEncoderTest.php | 59 ---- Tests/Encoder/SodiumPasswordEncoderTest.php | 88 ------ .../Encoder/TestPasswordEncoderInterface.php | 22 -- Tests/Encoder/UserPasswordEncoderTest.php | 98 ------- .../Constraints/UserPasswordValidator.php | 6 +- 32 files changed, 4 insertions(+), 2076 deletions(-) delete mode 100644 Encoder/BasePasswordEncoder.php delete mode 100644 Encoder/EncoderAwareInterface.php delete mode 100644 Encoder/EncoderFactory.php delete mode 100644 Encoder/EncoderFactoryInterface.php delete mode 100644 Encoder/LegacyEncoderTrait.php delete mode 100644 Encoder/LegacyPasswordHasherEncoder.php delete mode 100644 Encoder/MessageDigestPasswordEncoder.php delete mode 100644 Encoder/MigratingPasswordEncoder.php delete mode 100644 Encoder/NativePasswordEncoder.php delete mode 100644 Encoder/PasswordEncoderInterface.php delete mode 100644 Encoder/PasswordHasherAdapter.php delete mode 100644 Encoder/PasswordHasherEncoder.php delete mode 100644 Encoder/Pbkdf2PasswordEncoder.php delete mode 100644 Encoder/PlaintextPasswordEncoder.php delete mode 100644 Encoder/SelfSaltingEncoderInterface.php delete mode 100644 Encoder/SodiumPasswordEncoder.php delete mode 100644 Encoder/UserPasswordEncoder.php delete mode 100644 Encoder/UserPasswordEncoderInterface.php delete mode 100644 Tests/Encoder/EncoderFactoryTest.php delete mode 100644 Tests/Encoder/Fixtures/MyMessageDigestPasswordEncoder.php delete mode 100644 Tests/Encoder/MessageDigestPasswordEncoderTest.php delete mode 100644 Tests/Encoder/MigratingPasswordEncoderTest.php delete mode 100644 Tests/Encoder/NativePasswordEncoderTest.php delete mode 100644 Tests/Encoder/Pbkdf2PasswordEncoderTest.php delete mode 100644 Tests/Encoder/PlaintextPasswordEncoderTest.php delete mode 100644 Tests/Encoder/SodiumPasswordEncoderTest.php delete mode 100644 Tests/Encoder/TestPasswordEncoderInterface.php delete mode 100644 Tests/Encoder/UserPasswordEncoderTest.php diff --git a/Authentication/Provider/DaoAuthenticationProvider.php b/Authentication/Provider/DaoAuthenticationProvider.php index d83c1a0c..03f09641 100644 --- a/Authentication/Provider/DaoAuthenticationProvider.php +++ b/Authentication/Provider/DaoAuthenticationProvider.php @@ -13,7 +13,6 @@ use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; @@ -39,17 +38,10 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider private $hasherFactory; private $userProvider; - /** - * @param PasswordHasherFactoryInterface $hasherFactory - */ - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, $hasherFactory, bool $hideUserNotFoundExceptions = true) + public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, PasswordHasherFactoryInterface $hasherFactory, bool $hideUserNotFoundExceptions = true) { parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); - if ($hasherFactory instanceof EncoderFactoryInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'Passing a "%s" instance to the "%s" constructor is deprecated, use "%s" instead.', EncoderFactoryInterface::class, __CLASS__, PasswordHasherFactoryInterface::class); - } - $this->hasherFactory = $hasherFactory; $this->userProvider = $userProvider; } @@ -82,21 +74,6 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke trigger_deprecation('symfony/security-core', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user)); } - // deprecated since Symfony 5.3 - if ($this->hasherFactory instanceof EncoderFactoryInterface) { - $encoder = $this->hasherFactory->getEncoder($user); - - if (!$encoder->isPasswordValid($user->getPassword(), $presentedPassword, $salt)) { - throw new BadCredentialsException('The presented password is invalid.'); - } - - if ($this->userProvider instanceof PasswordUpgraderInterface && method_exists($encoder, 'needsRehash') && $encoder->needsRehash($user->getPassword())) { - $this->userProvider->upgradePassword($user, $encoder->encodePassword($presentedPassword, $user->getSalt())); - } - - return; - } - $hasher = $this->hasherFactory->getPasswordHasher($user); if (!$hasher->verify($user->getPassword(), $presentedPassword, $salt)) { diff --git a/CHANGELOG.md b/CHANGELOG.md index c60960c6..6e836c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * `TokenInterface` does not extend `Serializable` anymore + * Remove all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead 5.3 --- diff --git a/Encoder/BasePasswordEncoder.php b/Encoder/BasePasswordEncoder.php deleted file mode 100644 index 21c59b3c..00000000 --- a/Encoder/BasePasswordEncoder.php +++ /dev/null @@ -1,102 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\CheckPasswordLengthTrait; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', BasePasswordEncoder::class, CheckPasswordLengthTrait::class); - -/** - * BasePasswordEncoder is the base class for all password encoders. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use CheckPasswordLengthTrait instead - */ -abstract class BasePasswordEncoder implements PasswordEncoderInterface -{ - public const MAX_PASSWORD_LENGTH = 4096; - - /** - * {@inheritdoc} - */ - public function needsRehash(string $encoded): bool - { - return false; - } - - /** - * Demerges a merge password and salt string. - * - * @return array An array where the first element is the password and the second the salt - */ - protected function demergePasswordAndSalt(string $mergedPasswordSalt) - { - if (empty($mergedPasswordSalt)) { - return ['', '']; - } - - $password = $mergedPasswordSalt; - $salt = ''; - $saltBegins = strrpos($mergedPasswordSalt, '{'); - - if (false !== $saltBegins && $saltBegins + 1 < \strlen($mergedPasswordSalt)) { - $salt = substr($mergedPasswordSalt, $saltBegins + 1, -1); - $password = substr($mergedPasswordSalt, 0, $saltBegins); - } - - return [$password, $salt]; - } - - /** - * Merges a password and a salt. - * - * @return string a merged password and salt - * - * @throws \InvalidArgumentException - */ - protected function mergePasswordAndSalt(string $password, ?string $salt) - { - if (empty($salt)) { - return $password; - } - - if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) { - throw new \InvalidArgumentException('Cannot use { or } in salt.'); - } - - return $password.'{'.$salt.'}'; - } - - /** - * Compares two passwords. - * - * This method implements a constant-time algorithm to compare passwords to - * avoid (remote) timing attacks. - * - * @return bool true if the two passwords are the same, false otherwise - */ - protected function comparePasswords(string $password1, string $password2) - { - return hash_equals($password1, $password2); - } - - /** - * Checks if the password is too long. - * - * @return bool true if the password is too long, false otherwise - */ - protected function isPasswordTooLong(string $password) - { - return \strlen($password) > static::MAX_PASSWORD_LENGTH; - } -} diff --git a/Encoder/EncoderAwareInterface.php b/Encoder/EncoderAwareInterface.php deleted file mode 100644 index 70231e2c..00000000 --- a/Encoder/EncoderAwareInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\PasswordHasherAwareInterface; - -/** - * @author Christophe Coevoet - * - * @deprecated since Symfony 5.3, use {@link PasswordHasherAwareInterface} instead. - */ -interface EncoderAwareInterface -{ - /** - * Gets the name of the encoder used to encode the password. - * - * If the method returns null, the standard way to retrieve the encoder - * will be used instead. - * - * @return string|null - */ - public function getEncoderName(); -} diff --git a/Encoder/EncoderFactory.php b/Encoder/EncoderFactory.php deleted file mode 100644 index 526c461e..00000000 --- a/Encoder/EncoderFactory.php +++ /dev/null @@ -1,227 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\PasswordHasherAwareInterface; -use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory; -use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface; -use Symfony\Component\PasswordHasher\PasswordHasherInterface; -use Symfony\Component\Security\Core\Exception\LogicException; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', EncoderFactory::class, PasswordHasherFactory::class); - -/** - * A generic encoder factory implementation. - * - * @author Johannes M. Schmitt - * - * @deprecated since Symfony 5.3, use {@link PasswordHasherFactory} instead - */ -class EncoderFactory implements EncoderFactoryInterface -{ - private $encoders; - - public function __construct(array $encoders) - { - $this->encoders = $encoders; - } - - /** - * {@inheritdoc} - */ - public function getEncoder($user) - { - $encoderKey = null; - - if (($user instanceof PasswordHasherAwareInterface && null !== $encoderName = $user->getPasswordHasherName()) || ($user instanceof EncoderAwareInterface && null !== $encoderName = $user->getEncoderName())) { - if (!\array_key_exists($encoderName, $this->encoders)) { - throw new \RuntimeException(sprintf('The encoder "%s" was not configured.', $encoderName)); - } - - $encoderKey = $encoderName; - } else { - foreach ($this->encoders as $class => $encoder) { - if ((\is_object($user) && $user instanceof $class) || (!\is_object($user) && (is_subclass_of($user, $class) || $user == $class))) { - $encoderKey = $class; - break; - } - } - } - - if (null === $encoderKey) { - throw new \RuntimeException(sprintf('No encoder has been configured for account "%s".', \is_object($user) ? get_debug_type($user) : $user)); - } - - if (!$this->encoders[$encoderKey] instanceof PasswordEncoderInterface) { - if ($this->encoders[$encoderKey] instanceof LegacyPasswordHasherInterface) { - $this->encoders[$encoderKey] = new LegacyPasswordHasherEncoder($this->encoders[$encoderKey]); - } elseif ($this->encoders[$encoderKey] instanceof PasswordHasherInterface) { - $this->encoders[$encoderKey] = new PasswordHasherEncoder($this->encoders[$encoderKey]); - } else { - $this->encoders[$encoderKey] = $this->createEncoder($this->encoders[$encoderKey]); - } - } - - return $this->encoders[$encoderKey]; - } - - /** - * Creates the actual encoder instance. - * - * @throws \InvalidArgumentException - */ - private function createEncoder(array $config, bool $isExtra = false): PasswordEncoderInterface - { - if (isset($config['algorithm'])) { - $rawConfig = $config; - $config = $this->getEncoderConfigFromAlgorithm($config); - } - if (!isset($config['class'])) { - throw new \InvalidArgumentException('"class" must be set in '.json_encode($config)); - } - if (!isset($config['arguments'])) { - throw new \InvalidArgumentException('"arguments" must be set in '.json_encode($config)); - } - - $encoder = new $config['class'](...$config['arguments']); - - if ($isExtra || !\in_array($config['class'], [NativePasswordEncoder::class, SodiumPasswordEncoder::class], true)) { - return $encoder; - } - - if ($rawConfig ?? null) { - $extraEncoders = array_map(function (string $algo) use ($rawConfig): PasswordEncoderInterface { - $rawConfig['algorithm'] = $algo; - - return $this->createEncoder($rawConfig); - }, ['pbkdf2', $rawConfig['hash_algorithm'] ?? 'sha512']); - } else { - $extraEncoders = [new Pbkdf2PasswordEncoder(), new MessageDigestPasswordEncoder()]; - } - - return new MigratingPasswordEncoder($encoder, ...$extraEncoders); - } - - private function getEncoderConfigFromAlgorithm(array $config): array - { - if ('auto' === $config['algorithm']) { - $encoderChain = []; - // "plaintext" is not listed as any leaked hashes could then be used to authenticate directly - foreach ([SodiumPasswordEncoder::isSupported() ? 'sodium' : 'native', 'pbkdf2', $config['hash_algorithm']] as $algo) { - $config['algorithm'] = $algo; - $encoderChain[] = $this->createEncoder($config, true); - } - - return [ - 'class' => MigratingPasswordEncoder::class, - 'arguments' => $encoderChain, - ]; - } - - if ($fromEncoders = ($config['migrate_from'] ?? false)) { - unset($config['migrate_from']); - $encoderChain = [$this->createEncoder($config, true)]; - - foreach ($fromEncoders as $name) { - if ($encoder = $this->encoders[$name] ?? false) { - $encoder = $encoder instanceof PasswordEncoderInterface ? $encoder : $this->createEncoder($encoder, true); - } else { - $encoder = $this->createEncoder(['algorithm' => $name], true); - } - - $encoderChain[] = $encoder; - } - - return [ - 'class' => MigratingPasswordEncoder::class, - 'arguments' => $encoderChain, - ]; - } - - switch ($config['algorithm']) { - case 'plaintext': - return [ - 'class' => PlaintextPasswordEncoder::class, - 'arguments' => [$config['ignore_case']], - ]; - - case 'pbkdf2': - return [ - 'class' => Pbkdf2PasswordEncoder::class, - 'arguments' => [ - $config['hash_algorithm'] ?? 'sha512', - $config['encode_as_base64'] ?? true, - $config['iterations'] ?? 1000, - $config['key_length'] ?? 40, - ], - ]; - - case 'bcrypt': - $config['algorithm'] = 'native'; - $config['native_algorithm'] = \PASSWORD_BCRYPT; - - return $this->getEncoderConfigFromAlgorithm($config); - - case 'native': - return [ - 'class' => NativePasswordEncoder::class, - 'arguments' => [ - $config['time_cost'] ?? null, - (($config['memory_cost'] ?? 0) << 10) ?: null, - $config['cost'] ?? null, - ] + (isset($config['native_algorithm']) ? [3 => $config['native_algorithm']] : []), - ]; - - case 'sodium': - return [ - 'class' => SodiumPasswordEncoder::class, - 'arguments' => [ - $config['time_cost'] ?? null, - (($config['memory_cost'] ?? 0) << 10) ?: null, - ], - ]; - - case 'argon2i': - if (SodiumPasswordEncoder::isSupported() && !\defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) { - $config['algorithm'] = 'sodium'; - } elseif (\defined('PASSWORD_ARGON2I')) { - $config['algorithm'] = 'native'; - $config['native_algorithm'] = \PASSWORD_ARGON2I; - } else { - throw new LogicException(sprintf('Algorithm "argon2i" is not available. Either use %s"auto" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? '"argon2id", ' : '')); - } - - return $this->getEncoderConfigFromAlgorithm($config); - - case 'argon2id': - if (($hasSodium = SodiumPasswordEncoder::isSupported()) && \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13')) { - $config['algorithm'] = 'sodium'; - } elseif (\defined('PASSWORD_ARGON2ID')) { - $config['algorithm'] = 'native'; - $config['native_algorithm'] = \PASSWORD_ARGON2ID; - } else { - throw new LogicException(sprintf('Algorithm "argon2id" is not available. Either use %s"auto", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium ? '"argon2i", ' : '')); - } - - return $this->getEncoderConfigFromAlgorithm($config); - } - - return [ - 'class' => MessageDigestPasswordEncoder::class, - 'arguments' => [ - $config['algorithm'], - $config['encode_as_base64'] ?? true, - $config['iterations'] ?? 5000, - ], - ]; - } -} diff --git a/Encoder/EncoderFactoryInterface.php b/Encoder/EncoderFactoryInterface.php deleted file mode 100644 index 83dea6c7..00000000 --- a/Encoder/EncoderFactoryInterface.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\PasswordHasherFactoryInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', EncoderFactoryInterface::class, PasswordHasherFactoryInterface::class); - -/** - * EncoderFactoryInterface to support different encoders for different accounts. - * - * @author Johannes M. Schmitt - * - * @deprecated since Symfony 5.3, use {@link PasswordHasherFactoryInterface} instead - */ -interface EncoderFactoryInterface -{ - /** - * Returns the password encoder to use for the given account. - * - * @param UserInterface|string $user A UserInterface instance or a class name - * - * @return PasswordEncoderInterface - * - * @throws \RuntimeException when no password encoder could be found for the user - */ - public function getEncoder($user); -} diff --git a/Encoder/LegacyEncoderTrait.php b/Encoder/LegacyEncoderTrait.php deleted file mode 100644 index d1263213..00000000 --- a/Encoder/LegacyEncoderTrait.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * 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\PasswordHasher\Exception\InvalidPasswordException; -use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface; -use Symfony\Component\PasswordHasher\PasswordHasherInterface; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * @internal - */ -trait LegacyEncoderTrait -{ - /** - * @var PasswordHasherInterface|LegacyPasswordHasherInterface - */ - private $hasher; - - /** - * {@inheritdoc} - */ - public function encodePassword(string $raw, ?string $salt): string - { - try { - return $this->hasher->hash($raw, $salt); - } catch (InvalidPasswordException $e) { - throw new BadCredentialsException('Bad credentials.'); - } - } - - /** - * {@inheritdoc} - */ - public function isPasswordValid(string $encoded, string $raw, ?string $salt): bool - { - return $this->hasher->verify($encoded, $raw, $salt); - } - - /** - * {@inheritdoc} - */ - public function needsRehash(string $encoded): bool - { - return $this->hasher->needsRehash($encoded); - } -} diff --git a/Encoder/LegacyPasswordHasherEncoder.php b/Encoder/LegacyPasswordHasherEncoder.php deleted file mode 100644 index 7e57ff23..00000000 --- a/Encoder/LegacyPasswordHasherEncoder.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * 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\PasswordHasher\Exception\InvalidPasswordException; -use Symfony\Component\PasswordHasher\LegacyPasswordHasherInterface; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * Forward compatibility for new new PasswordHasher component. - * - * @author Alexander M. Turek - * - * @internal To be removed in Symfony 6 - */ -final class LegacyPasswordHasherEncoder implements PasswordEncoderInterface -{ - private $passwordHasher; - - public function __construct(LegacyPasswordHasherInterface $passwordHasher) - { - $this->passwordHasher = $passwordHasher; - } - - public function encodePassword(string $raw, ?string $salt): string - { - try { - return $this->passwordHasher->hash($raw, $salt); - } catch (InvalidPasswordException $e) { - throw new BadCredentialsException($e->getMessage(), $e->getCode(), $e); - } - } - - public function isPasswordValid(string $encoded, string $raw, ?string $salt): bool - { - return $this->passwordHasher->verify($encoded, $raw, $salt); - } - - public function needsRehash(string $encoded): bool - { - return $this->passwordHasher->needsRehash($encoded); - } -} diff --git a/Encoder/MessageDigestPasswordEncoder.php b/Encoder/MessageDigestPasswordEncoder.php deleted file mode 100644 index 2942c632..00000000 --- a/Encoder/MessageDigestPasswordEncoder.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\MessageDigestPasswordHasher; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', MessageDigestPasswordEncoder::class, MessageDigestPasswordHasher::class); - -/** - * MessageDigestPasswordEncoder uses a message digest algorithm. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use {@link MessageDigestPasswordHasher} instead - */ -class MessageDigestPasswordEncoder extends BasePasswordEncoder -{ - private $algorithm; - private $encodeHashAsBase64; - private $iterations = 1; - private $encodedLength = -1; - - /** - * @param string $algorithm The digest algorithm to use - * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash - * @param int $iterations The number of iterations to use to stretch the password hash - */ - public function __construct(string $algorithm = 'sha512', bool $encodeHashAsBase64 = true, int $iterations = 5000) - { - $this->algorithm = $algorithm; - $this->encodeHashAsBase64 = $encodeHashAsBase64; - - try { - $this->encodedLength = \strlen($this->encodePassword('', 'salt')); - } catch (\LogicException $e) { - // ignore algorithm not supported - } - - $this->iterations = $iterations; - } - - /** - * {@inheritdoc} - */ - public function encodePassword(string $raw, ?string $salt) - { - if ($this->isPasswordTooLong($raw)) { - throw new BadCredentialsException('Invalid password.'); - } - - if (!\in_array($this->algorithm, hash_algos(), true)) { - throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm)); - } - - $salted = $this->mergePasswordAndSalt($raw, $salt); - $digest = hash($this->algorithm, $salted, true); - - // "stretch" hash - for ($i = 1; $i < $this->iterations; ++$i) { - $digest = hash($this->algorithm, $digest.$salted, true); - } - - return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest); - } - - /** - * {@inheritdoc} - */ - public function isPasswordValid(string $encoded, string $raw, ?string $salt) - { - if (\strlen($encoded) !== $this->encodedLength || false !== strpos($encoded, '$')) { - return false; - } - - return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt)); - } -} diff --git a/Encoder/MigratingPasswordEncoder.php b/Encoder/MigratingPasswordEncoder.php deleted file mode 100644 index 53d3a58d..00000000 --- a/Encoder/MigratingPasswordEncoder.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\MigratingPasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', MigratingPasswordEncoder::class, MigratingPasswordHasher::class); - -/** - * Hashes passwords using the best available encoder. - * Validates them using a chain of encoders. - * - * /!\ Don't put a PlaintextPasswordEncoder in the list as that'd mean a leaked hash - * could be used to authenticate successfully without knowing the cleartext password. - * - * @author Nicolas Grekas - * - * @deprecated since Symfony 5.3, use {@link MigratingPasswordHasher} instead - */ -final class MigratingPasswordEncoder extends BasePasswordEncoder implements SelfSaltingEncoderInterface -{ - private $bestEncoder; - private $extraEncoders; - - public function __construct(PasswordEncoderInterface $bestEncoder, PasswordEncoderInterface ...$extraEncoders) - { - $this->bestEncoder = $bestEncoder; - $this->extraEncoders = $extraEncoders; - } - - /** - * {@inheritdoc} - */ - public function encodePassword(string $raw, ?string $salt): string - { - return $this->bestEncoder->encodePassword($raw, $salt); - } - - /** - * {@inheritdoc} - */ - public function isPasswordValid(string $encoded, string $raw, ?string $salt): bool - { - if ($this->bestEncoder->isPasswordValid($encoded, $raw, $salt)) { - return true; - } - - if (!$this->bestEncoder->needsRehash($encoded)) { - return false; - } - - foreach ($this->extraEncoders as $encoder) { - if ($encoder->isPasswordValid($encoded, $raw, $salt)) { - return true; - } - } - - return false; - } - - /** - * {@inheritdoc} - */ - public function needsRehash(string $encoded): bool - { - return $this->bestEncoder->needsRehash($encoded); - } -} diff --git a/Encoder/NativePasswordEncoder.php b/Encoder/NativePasswordEncoder.php deleted file mode 100644 index bc135bb1..00000000 --- a/Encoder/NativePasswordEncoder.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\NativePasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', NativePasswordEncoder::class, NativePasswordHasher::class); - -/** - * Hashes passwords using password_hash(). - * - * @author Elnur Abdurrakhimov - * @author Terje Bråten - * @author Nicolas Grekas - * - * @deprecated since Symfony 5.3, use {@link NativePasswordHasher} instead - */ -final class NativePasswordEncoder implements PasswordEncoderInterface, SelfSaltingEncoderInterface -{ - use LegacyEncoderTrait; - - /** - * @param string|null $algo An algorithm supported by password_hash() or null to use the stronger available algorithm - */ - public function __construct(int $opsLimit = null, int $memLimit = null, int $cost = null, string $algo = null) - { - $this->hasher = new NativePasswordHasher($opsLimit, $memLimit, $cost, $algo); - } -} diff --git a/Encoder/PasswordEncoderInterface.php b/Encoder/PasswordEncoderInterface.php deleted file mode 100644 index 45aa24ed..00000000 --- a/Encoder/PasswordEncoderInterface.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * 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\PasswordHasher\PasswordHasherInterface; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', PasswordEncoderInterface::class, PasswordHasherInterface::class); - -/** - * PasswordEncoderInterface is the interface for all encoders. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use {@link PasswordHasherInterface} instead - */ -interface PasswordEncoderInterface -{ - /** - * Encodes the raw password. - * - * @return string The encoded password - * - * @throws BadCredentialsException If the raw password is invalid, e.g. excessively long - * @throws \InvalidArgumentException If the salt is invalid - */ - public function encodePassword(string $raw, ?string $salt); - - /** - * Checks a raw password against an encoded password. - * - * @param string $encoded An encoded password - * @param string $raw A raw password - * @param string|null $salt The salt - * - * @return bool true if the password is valid, false otherwise - * - * @throws \InvalidArgumentException If the salt is invalid - */ - public function isPasswordValid(string $encoded, string $raw, ?string $salt); - - /** - * Checks if an encoded password would benefit from rehashing. - */ - public function needsRehash(string $encoded): bool; -} diff --git a/Encoder/PasswordHasherAdapter.php b/Encoder/PasswordHasherAdapter.php deleted file mode 100644 index a8546a4c..00000000 --- a/Encoder/PasswordHasherAdapter.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * 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\PasswordHasher\LegacyPasswordHasherInterface; - -/** - * Forward compatibility for new new PasswordHasher component. - * - * @author Alexander M. Turek - * - * @internal To be removed in Symfony 6 - */ -final class PasswordHasherAdapter implements LegacyPasswordHasherInterface -{ - private $passwordEncoder; - - public function __construct(PasswordEncoderInterface $passwordEncoder) - { - $this->passwordEncoder = $passwordEncoder; - } - - public function hash(string $plainPassword, string $salt = null): string - { - return $this->passwordEncoder->encodePassword($plainPassword, $salt); - } - - public function verify(string $hashedPassword, string $plainPassword, string $salt = null): bool - { - return $this->passwordEncoder->isPasswordValid($hashedPassword, $plainPassword, $salt); - } - - public function needsRehash(string $hashedPassword): bool - { - return $this->passwordEncoder->needsRehash($hashedPassword); - } -} diff --git a/Encoder/PasswordHasherEncoder.php b/Encoder/PasswordHasherEncoder.php deleted file mode 100644 index d37875dc..00000000 --- a/Encoder/PasswordHasherEncoder.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * 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\PasswordHasher\Exception\InvalidPasswordException; -use Symfony\Component\PasswordHasher\PasswordHasherInterface; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * Forward compatibility for new new PasswordHasher component. - * - * @author Alexander M. Turek - * - * @internal To be removed in Symfony 6 - */ -final class PasswordHasherEncoder implements PasswordEncoderInterface, SelfSaltingEncoderInterface -{ - private $passwordHasher; - - public function __construct(PasswordHasherInterface $passwordHasher) - { - $this->passwordHasher = $passwordHasher; - } - - public function encodePassword(string $raw, ?string $salt): string - { - if (null !== $salt) { - throw new \InvalidArgumentException('This password hasher does not support passing a salt.'); - } - - try { - return $this->passwordHasher->hash($raw); - } catch (InvalidPasswordException $e) { - throw new BadCredentialsException($e->getMessage(), $e->getCode(), $e); - } - } - - public function isPasswordValid(string $encoded, string $raw, ?string $salt): bool - { - if (null !== $salt) { - throw new \InvalidArgumentException('This password hasher does not support passing a salt.'); - } - - return $this->passwordHasher->verify($encoded, $raw); - } - - public function needsRehash(string $encoded): bool - { - return $this->passwordHasher->needsRehash($encoded); - } -} diff --git a/Encoder/Pbkdf2PasswordEncoder.php b/Encoder/Pbkdf2PasswordEncoder.php deleted file mode 100644 index d92c12fc..00000000 --- a/Encoder/Pbkdf2PasswordEncoder.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\Pbkdf2PasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', Pbkdf2PasswordEncoder::class, Pbkdf2PasswordHasher::class); - -/** - * Pbkdf2PasswordEncoder uses the PBKDF2 (Password-Based Key Derivation Function 2). - * - * Providing a high level of Cryptographic security, - * PBKDF2 is recommended by the National Institute of Standards and Technology (NIST). - * - * But also warrants a warning, using PBKDF2 (with a high number of iterations) slows down the process. - * PBKDF2 should be used with caution and care. - * - * @author Sebastiaan Stok - * @author Andrew Johnson - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use {@link Pbkdf2PasswordHasher} instead - */ -class Pbkdf2PasswordEncoder extends BasePasswordEncoder -{ - use LegacyEncoderTrait; - - /** - * @param string $algorithm The digest algorithm to use - * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash - * @param int $iterations The number of iterations to use to stretch the password hash - * @param int $length Length of derived key to create - */ - public function __construct(string $algorithm = 'sha512', bool $encodeHashAsBase64 = true, int $iterations = 1000, int $length = 40) - { - $this->hasher = new Pbkdf2PasswordHasher($algorithm, $encodeHashAsBase64, $iterations, $length); - } -} diff --git a/Encoder/PlaintextPasswordEncoder.php b/Encoder/PlaintextPasswordEncoder.php deleted file mode 100644 index 497e9f19..00000000 --- a/Encoder/PlaintextPasswordEncoder.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\PlaintextPasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', PlaintextPasswordEncoder::class, PlaintextPasswordHasher::class); - -/** - * PlaintextPasswordEncoder does not do any encoding but is useful in testing environments. - * - * As this encoder is not cryptographically secure, usage of it in production environments is discouraged. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use {@link PlaintextPasswordHasher} instead - */ -class PlaintextPasswordEncoder extends BasePasswordEncoder -{ - use LegacyEncoderTrait; - - /** - * @param bool $ignorePasswordCase Compare password case-insensitive - */ - public function __construct(bool $ignorePasswordCase = false) - { - $this->hasher = new PlaintextPasswordHasher($ignorePasswordCase); - } -} diff --git a/Encoder/SelfSaltingEncoderInterface.php b/Encoder/SelfSaltingEncoderInterface.php deleted file mode 100644 index b8740bc9..00000000 --- a/Encoder/SelfSaltingEncoderInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * 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\PasswordHasher\LegacyPasswordHasherInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" interface is deprecated, use "%s" on hasher implementations that deal with salts instead.', SelfSaltingEncoderInterface::class, LegacyPasswordHasherInterface::class); - -/** - * SelfSaltingEncoderInterface is a marker interface for encoders that do not - * require a user-generated salt. - * - * @author Zan Baldwin - * - * @deprecated since Symfony 5.3, use {@link LegacyPasswordHasherInterface} instead - */ -interface SelfSaltingEncoderInterface -{ -} diff --git a/Encoder/SodiumPasswordEncoder.php b/Encoder/SodiumPasswordEncoder.php deleted file mode 100644 index d2d71f48..00000000 --- a/Encoder/SodiumPasswordEncoder.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\SodiumPasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', SodiumPasswordEncoder::class, SodiumPasswordHasher::class); - -/** - * Hashes passwords using libsodium. - * - * @author Robin Chalas - * @author Zan Baldwin - * @author Dominik Müller - * - * @deprecated since Symfony 5.3, use {@link SodiumPasswordHasher} instead - */ -final class SodiumPasswordEncoder implements PasswordEncoderInterface, SelfSaltingEncoderInterface -{ - use LegacyEncoderTrait; - - public function __construct(int $opsLimit = null, int $memLimit = null) - { - $this->hasher = new SodiumPasswordHasher($opsLimit, $memLimit); - } - - public static function isSupported(): bool - { - return SodiumPasswordHasher::isSupported(); - } -} diff --git a/Encoder/UserPasswordEncoder.php b/Encoder/UserPasswordEncoder.php deleted file mode 100644 index 7b29918c..00000000 --- a/Encoder/UserPasswordEncoder.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\UserPasswordHasher; -use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', UserPasswordEncoder::class, UserPasswordHasher::class); - -/** - * A generic password encoder. - * - * @author Ariel Ferrandini - * - * @deprecated since Symfony 5.3, use {@link UserPasswordHasher} instead - */ -class UserPasswordEncoder implements UserPasswordEncoderInterface -{ - private $encoderFactory; - - public function __construct(EncoderFactoryInterface $encoderFactory) - { - $this->encoderFactory = $encoderFactory; - } - - /** - * {@inheritdoc} - */ - public function encodePassword(UserInterface $user, string $plainPassword) - { - $encoder = $this->encoderFactory->getEncoder($user); - - if (!$user instanceof PasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/password-hasher', '5.3', 'Not implementing the "%s" interface while using "%s" is deprecated, the "%s" class should implement it.', PasswordAuthenticatedUserInterface::class, __CLASS__, get_debug_type($user)); - } - - $salt = $user->getSalt(); - if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/password-hasher', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user)); - } - - return $encoder->encodePassword($plainPassword, $user->getSalt()); - } - - /** - * {@inheritdoc} - */ - public function isPasswordValid(UserInterface $user, string $raw) - { - if (null === $user->getPassword()) { - return false; - } - - $encoder = $this->encoderFactory->getEncoder($user); - - return $encoder->isPasswordValid($user->getPassword(), $raw, $user->getSalt()); - } - - /** - * {@inheritdoc} - */ - public function needsRehash(UserInterface $user): bool - { - if (null === $user->getPassword()) { - return false; - } - - $encoder = $this->encoderFactory->getEncoder($user); - - return $encoder->needsRehash($user->getPassword()); - } -} diff --git a/Encoder/UserPasswordEncoderInterface.php b/Encoder/UserPasswordEncoderInterface.php deleted file mode 100644 index 488777c1..00000000 --- a/Encoder/UserPasswordEncoderInterface.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\UserPasswordHasherInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" interface is deprecated, use "%s" instead.', UserPasswordEncoderInterface::class, UserPasswordHasherInterface::class); - -/** - * UserPasswordEncoderInterface is the interface for the password encoder service. - * - * @author Ariel Ferrandini - * - * @deprecated since Symfony 5.3, use {@link UserPasswordHasherInterface} instead - */ -interface UserPasswordEncoderInterface -{ - /** - * Encodes the plain password. - * - * @return string The encoded password - */ - public function encodePassword(UserInterface $user, string $plainPassword); - - /** - * @return bool true if the password is valid, false otherwise - */ - public function isPasswordValid(UserInterface $user, string $raw); - - /** - * Checks if an encoded password would benefit from rehashing. - */ - public function needsRehash(UserInterface $user): bool; -} diff --git a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php index 93c15d97..2270680a 100644 --- a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php +++ b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php @@ -18,7 +18,6 @@ use Symfony\Component\PasswordHasher\PasswordHasherInterface; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; @@ -53,18 +52,6 @@ public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface() $method->invoke($provider, 'fabien', $this->getSupportedToken()); } - public function testRetrieveUserWhenUsernameIsNotFoundWithLegacyEncoderFactory() - { - $this->expectException(UserNotFoundException::class); - $userProvider = new InMemoryUserProvider(); - - $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(EncoderFactoryInterface::class)); - $method = new \ReflectionMethod($provider, 'retrieveUser'); - $method->setAccessible(true); - - $method->invoke($provider, 'fabien', $this->getSupportedToken()); - } - public function testRetrieveUserWhenUsernameIsNotFound() { $this->expectException(UserNotFoundException::class); diff --git a/Tests/Encoder/EncoderFactoryTest.php b/Tests/Encoder/EncoderFactoryTest.php deleted file mode 100644 index 7b05c9be..00000000 --- a/Tests/Encoder/EncoderFactoryTest.php +++ /dev/null @@ -1,272 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\PasswordHasher\Hasher\MessageDigestPasswordHasher; -use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher; -use Symfony\Component\PasswordHasher\Hasher\PasswordHasherAwareInterface; -use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory; -use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher; -use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface; -use Symfony\Component\Security\Core\Encoder\EncoderFactory; -use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; -use Symfony\Component\Security\Core\Encoder\MigratingPasswordEncoder; -use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; -use Symfony\Component\Security\Core\Encoder\SelfSaltingEncoderInterface; -use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class EncoderFactoryTest extends TestCase -{ - public function testGetEncoderWithMessageDigestEncoder() - { - $factory = new EncoderFactory(['Symfony\Component\Security\Core\User\UserInterface' => [ - 'class' => 'Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder', - 'arguments' => ['sha512', true, 5], - ]]); - - $encoder = $factory->getEncoder($this->createMock(UserInterface::class)); - $expectedEncoder = new MessageDigestPasswordEncoder('sha512', true, 5); - - $this->assertEquals($expectedEncoder->encodePassword('foo', 'moo'), $encoder->encodePassword('foo', 'moo')); - } - - public function testGetEncoderWithService() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\User\UserInterface' => new MessageDigestPasswordEncoder('sha1'), - ]); - - $encoder = $factory->getEncoder($this->createMock(UserInterface::class)); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - - $encoder = $factory->getEncoder(new User('user', 'pass')); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testGetEncoderWithClassName() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\User\UserInterface' => new MessageDigestPasswordEncoder('sha1'), - ]); - - $encoder = $factory->getEncoder('Symfony\Component\Security\Core\Tests\Encoder\SomeChildUser'); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testGetEncoderConfiguredForConcreteClassWithService() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\User\User' => new MessageDigestPasswordEncoder('sha1'), - ]); - - $encoder = $factory->getEncoder(new User('user', 'pass')); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testGetEncoderConfiguredForConcreteClassWithClassName() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\Tests\Encoder\SomeUser' => new MessageDigestPasswordEncoder('sha1'), - ]); - - $encoder = $factory->getEncoder('Symfony\Component\Security\Core\Tests\Encoder\SomeChildUser'); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testGetNamedEncoderForEncoderAware() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha256'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha1'), - ]); - - $encoder = $factory->getEncoder(new EncAwareUser('user', 'pass')); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testGetNullNamedEncoderForEncoderAware() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha1'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha256'), - ]); - - $user = new EncAwareUser('user', 'pass'); - $user->encoderName = null; - $encoder = $factory->getEncoder($user); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testGetInvalidNamedEncoderForEncoderAware() - { - $this->expectException(\RuntimeException::class); - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha1'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha256'), - ]); - - $user = new EncAwareUser('user', 'pass'); - $user->encoderName = 'invalid_encoder_name'; - $factory->getEncoder($user); - } - - public function testGetEncoderForEncoderAwareWithClassName() - { - $factory = new EncoderFactory([ - 'Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser' => new MessageDigestPasswordEncoder('sha1'), - 'encoder_name' => new MessageDigestPasswordEncoder('sha256'), - ]); - - $encoder = $factory->getEncoder('Symfony\Component\Security\Core\Tests\Encoder\EncAwareUser'); - $expectedEncoder = new MessageDigestPasswordEncoder('sha1'); - $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', '')); - } - - public function testMigrateFrom() - { - if (!SodiumPasswordEncoder::isSupported()) { - $this->markTestSkipped('Sodium is not available'); - } - - $factory = new EncoderFactory([ - 'digest_encoder' => $digest = new MessageDigestPasswordEncoder('sha256'), - SomeUser::class => ['algorithm' => 'sodium', 'migrate_from' => ['bcrypt', 'digest_encoder']], - ]); - - $encoder = $factory->getEncoder(SomeUser::class); - $this->assertInstanceOf(MigratingPasswordEncoder::class, $encoder); - - $this->assertTrue($encoder->isPasswordValid((new SodiumPasswordEncoder())->encodePassword('foo', null), 'foo', null)); - $this->assertTrue($encoder->isPasswordValid((new NativePasswordEncoder(null, null, null, \PASSWORD_BCRYPT))->encodePassword('foo', null), 'foo', null)); - $this->assertTrue($encoder->isPasswordValid($digest->encodePassword('foo', null), 'foo', null)); - $this->assertStringStartsWith(\SODIUM_CRYPTO_PWHASH_STRPREFIX, $encoder->encodePassword('foo', null)); - } - - public function testDefaultMigratingEncoders() - { - $this->assertInstanceOf( - MigratingPasswordEncoder::class, - (new EncoderFactory([SomeUser::class => ['class' => NativePasswordEncoder::class, 'arguments' => []]]))->getEncoder(SomeUser::class) - ); - - $this->assertInstanceOf( - MigratingPasswordEncoder::class, - (new EncoderFactory([SomeUser::class => ['algorithm' => 'bcrypt', 'cost' => 11]]))->getEncoder(SomeUser::class) - ); - - if (!SodiumPasswordEncoder::isSupported()) { - return; - } - - $this->assertInstanceOf( - MigratingPasswordEncoder::class, - (new EncoderFactory([SomeUser::class => ['class' => SodiumPasswordEncoder::class, 'arguments' => []]]))->getEncoder(SomeUser::class) - ); - } - - public function testHasherAwareCompat() - { - $factory = new PasswordHasherFactory([ - 'encoder_name' => new MessageDigestPasswordHasher('sha1'), - ]); - - $encoder = $factory->getPasswordHasher(new HasherAwareUser('user', 'pass')); - $expectedEncoder = new MessageDigestPasswordHasher('sha1'); - $this->assertEquals($expectedEncoder->hash('foo', ''), $encoder->hash('foo', '')); - } - - public function testLegacyPasswordHasher() - { - $factory = new EncoderFactory([ - SomeUser::class => new PlaintextPasswordHasher(), - ]); - - $encoder = $factory->getEncoder(new SomeUser()); - self::assertNotInstanceOf(SelfSaltingEncoderInterface::class, $encoder); - self::assertSame('foo{bar}', $encoder->encodePassword('foo', 'bar')); - } - - public function testPasswordHasher() - { - $factory = new EncoderFactory([ - SomeUser::class => new NativePasswordHasher(), - ]); - - $encoder = $factory->getEncoder(new SomeUser()); - self::assertInstanceOf(SelfSaltingEncoderInterface::class, $encoder); - self::assertTrue($encoder->isPasswordValid($encoder->encodePassword('foo', null), 'foo', null)); - } -} - -class SomeUser implements UserInterface -{ - public function getRoles(): array - { - } - - public function getPassword(): ?string - { - } - - public function getSalt(): ?string - { - } - - public function getUsername(): string - { - } - - public function getUserIdentifier(): string - { - } - - public function eraseCredentials() - { - } -} - -class SomeChildUser extends SomeUser -{ -} - -class EncAwareUser extends SomeUser implements EncoderAwareInterface -{ - public $encoderName = 'encoder_name'; - - public function getEncoderName(): ?string - { - return $this->encoderName; - } -} - -class HasherAwareUser extends SomeUser implements PasswordHasherAwareInterface -{ - public $hasherName = 'encoder_name'; - - public function getPasswordHasherName(): ?string - { - return $this->hasherName; - } -} diff --git a/Tests/Encoder/Fixtures/MyMessageDigestPasswordEncoder.php b/Tests/Encoder/Fixtures/MyMessageDigestPasswordEncoder.php deleted file mode 100644 index 110541d5..00000000 --- a/Tests/Encoder/Fixtures/MyMessageDigestPasswordEncoder.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * 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\Encoder\Fixtures; - -use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; - -final class MyMessageDigestPasswordEncoder extends MessageDigestPasswordEncoder -{ - public function __construct() - { - parent::__construct('sha512', true, 1); - } - - protected function mergePasswordAndSalt(string $password, ?string $salt): string - { - return json_encode(['password' => $password, 'salt' => $salt]); - } - - protected function demergePasswordAndSalt(string $mergedPasswordSalt): array - { - ['password' => $password, 'salt' => $salt] = json_decode($mergedPasswordSalt, true); - - return [$password, $salt]; - } -} diff --git a/Tests/Encoder/MessageDigestPasswordEncoderTest.php b/Tests/Encoder/MessageDigestPasswordEncoderTest.php deleted file mode 100644 index a9753957..00000000 --- a/Tests/Encoder/MessageDigestPasswordEncoderTest.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Tests\Encoder\Fixtures\MyMessageDigestPasswordEncoder; - -/** - * @group legacy - */ -class MessageDigestPasswordEncoderTest extends TestCase -{ - public function testIsPasswordValid() - { - $encoder = new MessageDigestPasswordEncoder('sha256', false, 1); - - $this->assertTrue($encoder->isPasswordValid(hash('sha256', 'password'), 'password', '')); - } - - public function testEncodePassword() - { - $encoder = new MessageDigestPasswordEncoder('sha256', false, 1); - $this->assertSame(hash('sha256', 'password'), $encoder->encodePassword('password', '')); - - $encoder = new MessageDigestPasswordEncoder('sha256', true, 1); - $this->assertSame(base64_encode(hash('sha256', 'password', true)), $encoder->encodePassword('password', '')); - - $encoder = new MessageDigestPasswordEncoder('sha256', false, 2); - $this->assertSame(hash('sha256', hash('sha256', 'password', true).'password'), $encoder->encodePassword('password', '')); - } - - public function testEncodePasswordAlgorithmDoesNotExist() - { - $this->expectException(\LogicException::class); - $encoder = new MessageDigestPasswordEncoder('foobar'); - $encoder->encodePassword('password', ''); - } - - public function testEncodePasswordLength() - { - $this->expectException(BadCredentialsException::class); - $encoder = new MessageDigestPasswordEncoder(); - - $encoder->encodePassword(str_repeat('a', 5000), 'salt'); - } - - public function testCheckPasswordLength() - { - $encoder = new MessageDigestPasswordEncoder(); - - $this->assertFalse($encoder->isPasswordValid('encoded', str_repeat('a', 5000), 'salt')); - } - - public function testCustomEncoder() - { - $encoder = new MyMessageDigestPasswordEncoder(); - $encodedPassword = $encoder->encodePassword('p4ssw0rd', 's417'); - - $this->assertSame(base64_encode(hash('sha512', '{"password":"p4ssw0rd","salt":"s417"}', true)), $encodedPassword); - $this->assertTrue($encoder->isPasswordValid($encodedPassword, 'p4ssw0rd', 's417')); - } -} diff --git a/Tests/Encoder/MigratingPasswordEncoderTest.php b/Tests/Encoder/MigratingPasswordEncoderTest.php deleted file mode 100644 index fbaf89b0..00000000 --- a/Tests/Encoder/MigratingPasswordEncoderTest.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\MigratingPasswordEncoder; -use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; - -/** - * @group legacy - */ -class MigratingPasswordEncoderTest extends TestCase -{ - public function testValidation() - { - $bestEncoder = new NativePasswordEncoder(4, 12000, 4); - - $extraEncoder = $this->createMock(TestPasswordEncoderInterface::class); - $extraEncoder->expects($this->never())->method('encodePassword'); - $extraEncoder->expects($this->never())->method('isPasswordValid'); - $extraEncoder->expects($this->never())->method('needsRehash'); - - $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder); - - $this->assertTrue($encoder->needsRehash('foo')); - - $hash = $encoder->encodePassword('foo', 'salt'); - $this->assertFalse($encoder->needsRehash($hash)); - - $this->assertTrue($encoder->isPasswordValid($hash, 'foo', 'salt')); - $this->assertFalse($encoder->isPasswordValid($hash, 'bar', 'salt')); - } - - public function testFallback() - { - $bestEncoder = new NativePasswordEncoder(4, 12000, 4); - - $extraEncoder1 = $this->createMock(TestPasswordEncoderInterface::class); - $extraEncoder1->expects($this->any()) - ->method('isPasswordValid') - ->with('abc', 'foo', 'salt') - ->willReturn(true); - - $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder1); - - $this->assertTrue($encoder->isPasswordValid('abc', 'foo', 'salt')); - - $extraEncoder2 = $this->createMock(TestPasswordEncoderInterface::class); - $extraEncoder2->expects($this->any()) - ->method('isPasswordValid') - ->willReturn(false); - - $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder2); - - $this->assertFalse($encoder->isPasswordValid('abc', 'foo', 'salt')); - - $encoder = new MigratingPasswordEncoder($bestEncoder, $extraEncoder2, $extraEncoder1); - - $this->assertTrue($encoder->isPasswordValid('abc', 'foo', 'salt')); - } -} diff --git a/Tests/Encoder/NativePasswordEncoderTest.php b/Tests/Encoder/NativePasswordEncoderTest.php deleted file mode 100644 index 9d864dfc..00000000 --- a/Tests/Encoder/NativePasswordEncoderTest.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; - -/** - * @author Elnur Abdurrakhimov - * @group legacy - */ -class NativePasswordEncoderTest extends TestCase -{ - public function testCostBelowRange() - { - $this->expectException(\InvalidArgumentException::class); - new NativePasswordEncoder(null, null, 3); - } - - public function testCostAboveRange() - { - $this->expectException(\InvalidArgumentException::class); - new NativePasswordEncoder(null, null, 32); - } - - /** - * @dataProvider validRangeData - */ - public function testCostInRange($cost) - { - $this->assertInstanceOf(NativePasswordEncoder::class, new NativePasswordEncoder(null, null, $cost)); - } - - public function validRangeData() - { - $costs = range(4, 31); - array_walk($costs, function (&$cost) { $cost = [$cost]; }); - - return $costs; - } - - public function testValidation() - { - $encoder = new NativePasswordEncoder(); - $result = $encoder->encodePassword('password', null); - $this->assertTrue($encoder->isPasswordValid($result, 'password', null)); - $this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null)); - $this->assertFalse($encoder->isPasswordValid($result, '', null)); - } - - public function testNonArgonValidation() - { - $encoder = new NativePasswordEncoder(); - $this->assertTrue($encoder->isPasswordValid('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'password', null)); - $this->assertFalse($encoder->isPasswordValid('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'anotherPassword', null)); - $this->assertTrue($encoder->isPasswordValid('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'password', null)); - $this->assertFalse($encoder->isPasswordValid('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'anotherPassword', null)); - } - - public function testConfiguredAlgorithm() - { - $encoder = new NativePasswordEncoder(null, null, null, \PASSWORD_BCRYPT); - $result = $encoder->encodePassword('password', null); - $this->assertTrue($encoder->isPasswordValid($result, 'password', null)); - $this->assertStringStartsWith('$2', $result); - } - - public function testConfiguredAlgorithmWithLegacyConstValue() - { - $encoder = new NativePasswordEncoder(null, null, null, '1'); - $result = $encoder->encodePassword('password', null); - $this->assertTrue($encoder->isPasswordValid($result, 'password', null)); - $this->assertStringStartsWith('$2', $result); - } - - public function testCheckPasswordLength() - { - $encoder = new NativePasswordEncoder(null, null, 4); - $result = password_hash(str_repeat('a', 72), \PASSWORD_BCRYPT, ['cost' => 4]); - - $this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 73), 'salt')); - $this->assertTrue($encoder->isPasswordValid($result, str_repeat('a', 72), 'salt')); - } - - public function testNeedsRehash() - { - $encoder = new NativePasswordEncoder(4, 11000, 4); - - $this->assertTrue($encoder->needsRehash('dummyhash')); - - $hash = $encoder->encodePassword('foo', 'salt'); - $this->assertFalse($encoder->needsRehash($hash)); - - $encoder = new NativePasswordEncoder(5, 11000, 5); - $this->assertTrue($encoder->needsRehash($hash)); - } -} diff --git a/Tests/Encoder/Pbkdf2PasswordEncoderTest.php b/Tests/Encoder/Pbkdf2PasswordEncoderTest.php deleted file mode 100644 index 000e07d6..00000000 --- a/Tests/Encoder/Pbkdf2PasswordEncoderTest.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * @group legacy - */ -class Pbkdf2PasswordEncoderTest extends TestCase -{ - public function testIsPasswordValid() - { - $encoder = new Pbkdf2PasswordEncoder('sha256', false, 1, 40); - - $this->assertTrue($encoder->isPasswordValid('c1232f10f62715fda06ae7c0a2037ca19b33cf103b727ba56d870c11f290a2ab106974c75607c8a3', 'password', '')); - } - - public function testEncodePassword() - { - $encoder = new Pbkdf2PasswordEncoder('sha256', false, 1, 40); - $this->assertSame('c1232f10f62715fda06ae7c0a2037ca19b33cf103b727ba56d870c11f290a2ab106974c75607c8a3', $encoder->encodePassword('password', '')); - - $encoder = new Pbkdf2PasswordEncoder('sha256', true, 1, 40); - $this->assertSame('wSMvEPYnFf2gaufAogN8oZszzxA7cnulbYcMEfKQoqsQaXTHVgfIow==', $encoder->encodePassword('password', '')); - - $encoder = new Pbkdf2PasswordEncoder('sha256', false, 2, 40); - $this->assertSame('8bc2f9167a81cdcfad1235cd9047f1136271c1f978fcfcb35e22dbeafa4634f6fd2214218ed63ebb', $encoder->encodePassword('password', '')); - } - - public function testEncodePasswordAlgorithmDoesNotExist() - { - $this->expectException(\LogicException::class); - $encoder = new Pbkdf2PasswordEncoder('foobar'); - $encoder->encodePassword('password', ''); - } - - public function testEncodePasswordLength() - { - $this->expectException(BadCredentialsException::class); - $encoder = new Pbkdf2PasswordEncoder('foobar'); - - $encoder->encodePassword(str_repeat('a', 5000), 'salt'); - } - - public function testCheckPasswordLength() - { - $encoder = new Pbkdf2PasswordEncoder('foobar'); - - $this->assertFalse($encoder->isPasswordValid('encoded', str_repeat('a', 5000), 'salt')); - } -} diff --git a/Tests/Encoder/PlaintextPasswordEncoderTest.php b/Tests/Encoder/PlaintextPasswordEncoderTest.php deleted file mode 100644 index 39804403..00000000 --- a/Tests/Encoder/PlaintextPasswordEncoderTest.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * @group legacy - */ -class PlaintextPasswordEncoderTest extends TestCase -{ - public function testIsPasswordValid() - { - $encoder = new PlaintextPasswordEncoder(); - - $this->assertTrue($encoder->isPasswordValid('foo', 'foo', '')); - $this->assertFalse($encoder->isPasswordValid('bar', 'foo', '')); - $this->assertFalse($encoder->isPasswordValid('FOO', 'foo', '')); - - $encoder = new PlaintextPasswordEncoder(true); - - $this->assertTrue($encoder->isPasswordValid('foo', 'foo', '')); - $this->assertFalse($encoder->isPasswordValid('bar', 'foo', '')); - $this->assertTrue($encoder->isPasswordValid('FOO', 'foo', '')); - } - - public function testEncodePassword() - { - $encoder = new PlaintextPasswordEncoder(); - - $this->assertSame('foo', $encoder->encodePassword('foo', '')); - } - - public function testEncodePasswordLength() - { - $this->expectException(BadCredentialsException::class); - $encoder = new PlaintextPasswordEncoder(); - - $encoder->encodePassword(str_repeat('a', 5000), 'salt'); - } - - public function testCheckPasswordLength() - { - $encoder = new PlaintextPasswordEncoder(); - - $this->assertFalse($encoder->isPasswordValid('encoded', str_repeat('a', 5000), 'salt')); - } -} diff --git a/Tests/Encoder/SodiumPasswordEncoderTest.php b/Tests/Encoder/SodiumPasswordEncoderTest.php deleted file mode 100644 index 4bae5f89..00000000 --- a/Tests/Encoder/SodiumPasswordEncoderTest.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * @group legacy - */ -class SodiumPasswordEncoderTest extends TestCase -{ - protected function setUp(): void - { - if (!SodiumPasswordEncoder::isSupported()) { - $this->markTestSkipped('Libsodium is not available.'); - } - } - - public function testValidation() - { - $encoder = new SodiumPasswordEncoder(); - $result = $encoder->encodePassword('password', null); - $this->assertTrue($encoder->isPasswordValid($result, 'password', null)); - $this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null)); - $this->assertFalse($encoder->isPasswordValid($result, '', null)); - } - - public function testBCryptValidation() - { - $encoder = new SodiumPasswordEncoder(); - $this->assertTrue($encoder->isPasswordValid('$2y$04$M8GDODMoGQLQRpkYCdoJh.lbiZPee3SZI32RcYK49XYTolDGwoRMm', 'abc', null)); - } - - public function testNonArgonValidation() - { - $encoder = new SodiumPasswordEncoder(); - $this->assertTrue($encoder->isPasswordValid('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'password', null)); - $this->assertFalse($encoder->isPasswordValid('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'anotherPassword', null)); - $this->assertTrue($encoder->isPasswordValid('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'password', null)); - $this->assertFalse($encoder->isPasswordValid('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'anotherPassword', null)); - } - - public function testEncodePasswordLength() - { - $this->expectException(BadCredentialsException::class); - $encoder = new SodiumPasswordEncoder(); - $encoder->encodePassword(str_repeat('a', 4097), 'salt'); - } - - public function testCheckPasswordLength() - { - $encoder = new SodiumPasswordEncoder(); - $result = $encoder->encodePassword(str_repeat('a', 4096), null); - $this->assertFalse($encoder->isPasswordValid($result, str_repeat('a', 4097), null)); - $this->assertTrue($encoder->isPasswordValid($result, str_repeat('a', 4096), null)); - } - - public function testUserProvidedSaltIsNotUsed() - { - $encoder = new SodiumPasswordEncoder(); - $result = $encoder->encodePassword('password', 'salt'); - $this->assertTrue($encoder->isPasswordValid($result, 'password', 'anotherSalt')); - } - - public function testNeedsRehash() - { - $encoder = new SodiumPasswordEncoder(4, 11000); - - $this->assertTrue($encoder->needsRehash('dummyhash')); - - $hash = $encoder->encodePassword('foo', 'salt'); - $this->assertFalse($encoder->needsRehash($hash)); - - $encoder = new SodiumPasswordEncoder(5, 11000); - $this->assertTrue($encoder->needsRehash($hash)); - } -} diff --git a/Tests/Encoder/TestPasswordEncoderInterface.php b/Tests/Encoder/TestPasswordEncoderInterface.php deleted file mode 100644 index 3764038e..00000000 --- a/Tests/Encoder/TestPasswordEncoderInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * 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\Encoder; - -use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; - -/** - * @group legacy - */ -interface TestPasswordEncoderInterface extends PasswordEncoderInterface -{ - public function needsRehash(string $encoded): bool; -} diff --git a/Tests/Encoder/UserPasswordEncoderTest.php b/Tests/Encoder/UserPasswordEncoderTest.php deleted file mode 100644 index 6f52fbf1..00000000 --- a/Tests/Encoder/UserPasswordEncoderTest.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * 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\Encoder; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; -use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder; -use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; -use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class UserPasswordEncoderTest extends TestCase -{ - public function testEncodePassword() - { - $userMock = $this->createMock(UserInterface::class); - $userMock->expects($this->any()) - ->method('getSalt') - ->willReturn('userSalt'); - - $mockEncoder = $this->createMock(PasswordEncoderInterface::class); - $mockEncoder->expects($this->any()) - ->method('encodePassword') - ->with($this->equalTo('plainPassword'), $this->equalTo('userSalt')) - ->willReturn('encodedPassword'); - - $mockEncoderFactory = $this->createMock(EncoderFactoryInterface::class); - $mockEncoderFactory->expects($this->any()) - ->method('getEncoder') - ->with($this->equalTo($userMock)) - ->willReturn($mockEncoder); - - $passwordEncoder = new UserPasswordEncoder($mockEncoderFactory); - - $encoded = $passwordEncoder->encodePassword($userMock, 'plainPassword'); - $this->assertEquals('encodedPassword', $encoded); - } - - public function testIsPasswordValid() - { - $userMock = $this->createMock(UserInterface::class); - $userMock->expects($this->any()) - ->method('getSalt') - ->willReturn('userSalt'); - $userMock->expects($this->any()) - ->method('getPassword') - ->willReturn('encodedPassword'); - - $mockEncoder = $this->createMock(PasswordEncoderInterface::class); - $mockEncoder->expects($this->any()) - ->method('isPasswordValid') - ->with($this->equalTo('encodedPassword'), $this->equalTo('plainPassword'), $this->equalTo('userSalt')) - ->willReturn(true); - - $mockEncoderFactory = $this->createMock(EncoderFactoryInterface::class); - $mockEncoderFactory->expects($this->any()) - ->method('getEncoder') - ->with($this->equalTo($userMock)) - ->willReturn($mockEncoder); - - $passwordEncoder = new UserPasswordEncoder($mockEncoderFactory); - - $isValid = $passwordEncoder->isPasswordValid($userMock, 'plainPassword'); - $this->assertTrue($isValid); - } - - public function testNeedsRehash() - { - $user = new User('username', null); - $encoder = new NativePasswordEncoder(4, 20000, 4); - - $mockEncoderFactory = $this->createMock(EncoderFactoryInterface::class); - $mockEncoderFactory->expects($this->any()) - ->method('getEncoder') - ->with($user) - ->will($this->onConsecutiveCalls($encoder, $encoder, new NativePasswordEncoder(5, 20000, 5), $encoder)); - - $passwordEncoder = new UserPasswordEncoder($mockEncoderFactory); - - $user->setPassword($passwordEncoder->encodePassword($user, 'foo', 'salt')); - $this->assertFalse($passwordEncoder->needsRehash($user)); - $this->assertTrue($passwordEncoder->needsRehash($user)); - $this->assertFalse($passwordEncoder->needsRehash($user)); - } -} diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 7fb653c4..e0230da0 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -13,8 +13,6 @@ use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; -use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -68,9 +66,9 @@ public function validate(mixed $password, Constraint $constraint) trigger_deprecation('symfony/security-core', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user)); } - $hasher = $this->hasherFactory instanceof EncoderFactoryInterface ? $this->hasherFactory->getEncoder($user) : $this->hasherFactory->getPasswordHasher($user); + $hasher = $this->hasherFactory->getPasswordHasher($user); - if (null === $user->getPassword() || !($hasher instanceof PasswordEncoderInterface ? $hasher->isPasswordValid($user->getPassword(), $password, $user->getSalt()) : $hasher->verify($user->getPassword(), $password, $user->getSalt()))) { + if (null === $user->getPassword() || !$hasher->verify($user->getPassword(), $password, $user->getSalt())) { $this->context->addViolation($constraint->message); } } From 4e3065540553187bb6803d14820278b94076dc91 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Mon, 5 Jul 2021 00:26:05 +0200 Subject: [PATCH 011/138] [Security] Remove getPassword() and getSalt() from UserInterface --- .../Provider/DaoAuthenticationProvider.php | 18 +++++++-------- Authentication/Token/AbstractToken.php | 19 +++++++++------ CHANGELOG.md | 4 +++- .../DaoAuthenticationProviderTest.php | 18 ++++++--------- User/ChainUserProvider.php | 10 +------- User/PasswordUpgraderInterface.php | 13 +++++++---- User/UserInterface.php | 23 ------------------- .../Constraints/UserPasswordValidator.php | 14 ++--------- 8 files changed, 41 insertions(+), 78 deletions(-) diff --git a/Authentication/Provider/DaoAuthenticationProvider.php b/Authentication/Provider/DaoAuthenticationProvider.php index 03f09641..323418b3 100644 --- a/Authentication/Provider/DaoAuthenticationProvider.php +++ b/Authentication/Provider/DaoAuthenticationProvider.php @@ -15,6 +15,7 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; @@ -51,8 +52,13 @@ public function __construct(UserProviderInterface $userProvider, UserCheckerInte */ protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) { + if (!$user instanceof PasswordAuthenticatedUserInterface) { + throw new InvalidArgumentException(sprintf('Class "%s" must implement "%s" for using password-based authentication.', get_debug_type($user), PasswordAuthenticatedUserInterface::class)); + } + $currentUser = $token->getUser(); - if ($currentUser instanceof UserInterface) { + + if ($currentUser instanceof PasswordAuthenticatedUserInterface) { if ($currentUser->getPassword() !== $user->getPassword()) { throw new BadCredentialsException('The credentials were changed from another session.'); } @@ -65,15 +71,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke throw new BadCredentialsException('The presented password is invalid.'); } - if (!$user instanceof PasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'Using password-based authentication listeners while not implementing "%s" interface from class "%s" is deprecated.', PasswordAuthenticatedUserInterface::class, get_debug_type($user)); - } - - $salt = $user->getSalt(); - if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user)); - } - + $salt = $user instanceof LegacyPasswordAuthenticatedUserInterface ? $user->getSalt() : null; $hasher = $this->hasherFactory->getPasswordHasher($user); if (!$hasher->verify($user->getPassword(), $presentedPassword, $salt)) { diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 507e0d48..3d16e5b0 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Authentication\Token; use Symfony\Component\Security\Core\User\EquatableInterface; +use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -273,14 +274,18 @@ private function hasUserChanged(UserInterface $user): bool return !(bool) $this->user->isEqualTo($user); } - // @deprecated since Symfony 5.3, check for PasswordAuthenticatedUserInterface on both user objects before comparing passwords - if ($this->user->getPassword() !== $user->getPassword()) { - return true; - } + if ($this->user instanceof PasswordAuthenticatedUserInterface || $user instanceof PasswordAuthenticatedUserInterface) { + if (!$this->user instanceof PasswordAuthenticatedUserInterface || !$user instanceof PasswordAuthenticatedUserInterface || $this->user->getPassword() !== $user->getPassword()) { + return true; + } - // @deprecated since Symfony 5.3, check for LegacyPasswordAuthenticatedUserInterface on both user objects before comparing salts - if ($this->user->getSalt() !== $user->getSalt()) { - return true; + if ($this->user instanceof LegacyPasswordAuthenticatedUserInterface xor $user instanceof LegacyPasswordAuthenticatedUserInterface) { + return true; + } + + if ($this->user instanceof LegacyPasswordAuthenticatedUserInterface && $user instanceof LegacyPasswordAuthenticatedUserInterface && $this->user->getSalt() !== $user->getSalt()) { + return true; + } } $userRoles = array_map('strval', (array) $user->getRoles()); diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e836c30..bd41fd69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ CHANGELOG --- * `TokenInterface` does not extend `Serializable` anymore - * Remove all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead + * Remove all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead + * Remove methods `getPassword()` and `getSalt()` from `UserInterface`, use `PasswordAuthenticatedUserInterface` + or `LegacyPasswordAuthenticatedUserInterface` instead 5.3 --- diff --git a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php index 90702a2f..3d4f6048 100644 --- a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php +++ b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php @@ -23,6 +23,7 @@ use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -188,7 +189,7 @@ public function testCheckAuthenticationWhenCredentialsAreNotValid() public function testCheckAuthenticationDoesNotReauthenticateWhenPasswordHasChanged() { $this->expectException(BadCredentialsException::class); - $user = $this->createMock(UserInterface::class); + $user = $this->createMock(TestUser::class); $user->expects($this->once()) ->method('getPassword') ->willReturn('foo') @@ -199,7 +200,7 @@ public function testCheckAuthenticationDoesNotReauthenticateWhenPasswordHasChang ->method('getUser') ->willReturn($user); - $dbUser = $this->createMock(UserInterface::class); + $dbUser = $this->createMock(TestUser::class); $dbUser->expects($this->once()) ->method('getPassword') ->willReturn('newFoo') @@ -213,7 +214,7 @@ public function testCheckAuthenticationDoesNotReauthenticateWhenPasswordHasChang public function testCheckAuthenticationWhenTokenNeedsReauthenticationWorksWithoutOriginalCredentials() { - $user = $this->createMock(UserInterface::class); + $user = $this->createMock(TestUser::class); $user->expects($this->once()) ->method('getPassword') ->willReturn('foo') @@ -224,7 +225,7 @@ public function testCheckAuthenticationWhenTokenNeedsReauthenticationWorksWithou ->method('getUser') ->willReturn($user); - $dbUser = $this->createMock(UserInterface::class); + $dbUser = $this->createMock(TestUser::class); $dbUser->expects($this->once()) ->method('getPassword') ->willReturn('foo') @@ -338,7 +339,7 @@ protected function getProvider($user = null, $userChecker = null, $passwordHashe } } -class TestUser implements UserInterface +class TestUser implements PasswordAuthenticatedUserInterface, UserInterface { public function getRoles(): array { @@ -350,11 +351,6 @@ public function getPassword(): ?string return 'secret'; } - public function getSalt(): ?string - { - return null; - } - public function getUsername(): string { return 'jane_doe'; @@ -371,7 +367,7 @@ public function eraseCredentials() } interface PasswordUpgraderProvider extends UserProviderInterface, PasswordUpgraderInterface { - public function upgradePassword($user, string $newHashedPassword): void; + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void; public function loadUserByIdentifier(string $identifier): UserInterface; } diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 38e92f2e..407d4b7b 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -128,16 +128,8 @@ public function supportsClass(string $class) /** * {@inheritdoc} */ - public function upgradePassword($user, string $newHashedPassword): void + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void { - if (!$user instanceof PasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'The "%s::upgradePassword()" method expects an instance of "%s" as first argument, the "%s" class should implement it.', PasswordUpgraderInterface::class, PasswordAuthenticatedUserInterface::class, get_debug_type($user)); - - if (!$user instanceof UserInterface) { - throw new \TypeError(sprintf('The "%s::upgradePassword()" method expects an instance of "%s" as first argument, "%s" given.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); - } - } - foreach ($this->providers as $provider) { if ($provider instanceof PasswordUpgraderInterface) { try { diff --git a/User/PasswordUpgraderInterface.php b/User/PasswordUpgraderInterface.php index 16195fad..fd21f14a 100644 --- a/User/PasswordUpgraderInterface.php +++ b/User/PasswordUpgraderInterface.php @@ -13,12 +13,15 @@ /** * @author Nicolas Grekas - * - * @method void upgradePassword(PasswordAuthenticatedUserInterface|UserInterface $user, string $newHashedPassword) Upgrades the hashed password of a user, typically for using a better hash algorithm. - * This method should persist the new password in the user storage and update the $user object accordingly. - * Because you don't want your users not being able to log in, this method should be opportunistic: - * it's fine if it does nothing or if it fails without throwing any exception. */ interface PasswordUpgraderInterface { + /** + * Upgrades the hashed password of a user, typically for using a better hash algorithm. + * + * This method should persist the new password in the user storage and update the $user object accordingly. + * Because you don't want your users not being able to log in, this method should be opportunistic: + * it's fine if it does nothing or if it fails without throwing any exception. + */ + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void; } diff --git a/User/UserInterface.php b/User/UserInterface.php index f30d36e1..39f51771 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -48,29 +48,6 @@ interface UserInterface */ public function getRoles(); - /** - * Returns the password used to authenticate the user. - * - * This should be the hashed password. On authentication, a plain-text - * password will be hashed, and then compared to this value. - * - * This method is deprecated since Symfony 5.3, implement it from {@link PasswordAuthenticatedUserInterface} instead. - * - * @return string|null The hashed password if any - */ - public function getPassword(); - - /** - * Returns the salt that was originally used to hash the password. - * - * This can return null if the password was not hashed using a salt. - * - * This method is deprecated since Symfony 5.3, implement it from {@link LegacyPasswordAuthenticatedUserInterface} instead. - * - * @return string|null The salt - */ - public function getSalt(); - /** * Removes sensitive data from the user. * diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 2e0c49f9..42b13921 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -15,7 +15,6 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; @@ -53,22 +52,13 @@ public function validate(mixed $password, Constraint $constraint) $user = $this->tokenStorage->getToken()->getUser(); - if (!$user instanceof UserInterface) { - throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.'); - } - if (!$user instanceof PasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'Using the "%s" validation constraint without implementing the "%s" interface is deprecated, the "%s" class should implement it.', UserPassword::class, PasswordAuthenticatedUserInterface::class, get_debug_type($user)); - } - - $salt = $user->getSalt(); - if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) { - trigger_deprecation('symfony/security-core', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user)); + throw new ConstraintDefinitionException(sprintf('The "%s" class must implement the "%s" interface.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); } $hasher = $this->hasherFactory->getPasswordHasher($user); - if (null === $user->getPassword() || !$hasher->verify($user->getPassword(), $password, $user->getSalt())) { + if (null === $user->getPassword() || !$hasher->verify($user->getPassword(), $password, $user instanceof LegacyPasswordAuthenticatedUserInterface ? $user->getSalt() : null)) { $this->context->addViolation($constraint->message); } } From f40ad8d0339e2969549ee949d8213c4f336b188f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 Jul 2021 11:26:55 +0200 Subject: [PATCH 012/138] Add return types, round 1 --- Authorization/Voter/TraceableVoter.php | 2 +- .../AuthenticationTrustResolverTest.php | 4 ++-- .../DaoAuthenticationProviderTest.php | 19 +------------------ .../Token/AbstractTokenTest.php | 6 +++--- 4 files changed, 7 insertions(+), 24 deletions(-) diff --git a/Authorization/Voter/TraceableVoter.php b/Authorization/Voter/TraceableVoter.php index c1f23ed2..02b0eb9a 100644 --- a/Authorization/Voter/TraceableVoter.php +++ b/Authorization/Voter/TraceableVoter.php @@ -33,7 +33,7 @@ public function __construct(VoterInterface $voter, EventDispatcherInterface $eve $this->eventDispatcher = $eventDispatcher; } - public function vote(TokenInterface $token, mixed $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes): int { $result = $this->voter->vote($token, $subject, $attributes); diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index adb14975..d73526f7 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -139,7 +139,7 @@ public function getRoleNames(): array { } - public function getCredentials() + public function getCredentials(): mixed { } @@ -183,7 +183,7 @@ public function hasAttribute(string $name): bool { } - public function getAttribute(string $name) + public function getAttribute(string $name): mixed { } diff --git a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php index 3d4f6048..f43d7f06 100644 --- a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php +++ b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php @@ -36,23 +36,6 @@ class DaoAuthenticationProviderTest extends TestCase { use ExpectDeprecationTrait; - public function testRetrieveUserWhenProviderDoesNotReturnAnUserInterface() - { - $this->expectException(AuthenticationServiceException::class); - $userProvider = $this->createMock(DaoAuthenticationProviderTest_UserProvider::class); - $userProvider->expects($this->once()) - ->method('loadUserByUsername') - ->willReturn('fabien') - ; - $provider = $this->getProvider(null, null, null, $userProvider); - $method = new \ReflectionMethod($provider, 'retrieveUser'); - $method->setAccessible(true); - - $this->expectDeprecation('Since symfony/security-core 5.3: Not implementing method "loadUserByIdentifier()" in user provider "'.get_debug_type($userProvider).'" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.'); - - $method->invoke($provider, 'fabien', $this->getSupportedToken()); - } - public function testRetrieveUserWhenUsernameIsNotFound() { $this->expectException(UserNotFoundException::class); @@ -374,5 +357,5 @@ public function loadUserByIdentifier(string $identifier): UserInterface; interface DaoAuthenticationProviderTest_UserProvider extends UserProviderInterface { - public function loadUserByUsername($username); + public function loadUserByUsername($username): UserInterface; } diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index 928ff9b5..b141b4c3 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -247,7 +247,7 @@ public function __construct($name, array $roles = []) $this->roles = $roles; } - public function getUsername() + public function getUsername(): string { return $this->name; } @@ -262,7 +262,7 @@ public function getPassword() return '***'; } - public function getRoles() + public function getRoles(): array { if (empty($this->roles)) { return ['ROLE_USER']; @@ -305,7 +305,7 @@ public function __unserialize(array $data): void parent::__unserialize($parentState); } - public function getCredentials() + public function getCredentials(): mixed { } } From 8eb7267543b4793b8ed9b3ab1514d4ca4a5acddd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 13 Jul 2021 13:47:05 +0200 Subject: [PATCH 013/138] [Contracts] add return types and bump to v3 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 9cb24782..9eeac437 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,13 @@ ], "require": { "php": ">=8.0.2", - "symfony/event-dispatcher-contracts": "^1.1|^2", - "symfony/service-contracts": "^1.1.6|^2", - "symfony/deprecation-contracts": "^2.1", + "symfony/event-dispatcher-contracts": "^1.1|^2.0|^3.0", + "symfony/service-contracts": "^1.1.6|^2.0|^3.0", + "symfony/deprecation-contracts": "^2.1|^3.0", "symfony/password-hasher": "^5.4|^6.0" }, "require-dev": { - "psr/container": "^1.0|^2.0", + "psr/container": "^1.1|^2.0", "psr/cache": "^1.0|^2.0|^3.0", "symfony/cache": "^5.4|^6.0", "symfony/event-dispatcher": "^5.4|^6.0", From 2d2233faa16c7c8fabbc28994315605470b3176f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 20 Jul 2021 15:07:23 +0200 Subject: [PATCH 014/138] Add return type unions to private/internal/final/test methods --- Authentication/AuthenticationManagerInterface.php | 2 +- Tests/Authentication/AuthenticationTrustResolverTest.php | 3 ++- Tests/Authentication/Token/AbstractTokenTest.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Authentication/AuthenticationManagerInterface.php b/Authentication/AuthenticationManagerInterface.php index 6776ee78..bfdc95d1 100644 --- a/Authentication/AuthenticationManagerInterface.php +++ b/Authentication/AuthenticationManagerInterface.php @@ -20,7 +20,7 @@ * * @author Fabien Potencier * - * @internal since Symfony 5.3 + * @internal */ interface AuthenticationManagerInterface { diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index d73526f7..7b098ff3 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\User\UserInterface; class AuthenticationTrustResolverTest extends TestCase { @@ -143,7 +144,7 @@ public function getCredentials(): mixed { } - public function getUser() + public function getUser(): string|\Stringable|UserInterface { } diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index e2abe5db..a31cf555 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -261,7 +261,7 @@ public function getUsername(): string return $this->name; } - public function getUserIdentifier() + public function getUserIdentifier(): string { return $this->name; } From 8eb0d22084b2888d7a65eb29bf84da07662e134f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 21 Jul 2021 12:04:28 +0200 Subject: [PATCH 015/138] Narrow existing return types on private/internal/final/test methods --- Tests/Authentication/Token/AbstractTokenTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index a31cf555..b3d347b8 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -266,7 +266,7 @@ public function getUserIdentifier(): string return $this->name; } - public function getPassword() + public function getPassword(): ?string { return '***'; } @@ -284,7 +284,7 @@ public function eraseCredentials() { } - public function getSalt() + public function getSalt(): ?string { return null; } From 961d061dd60fc08def6fa11b4a1b9ba767918088 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 16 Aug 2021 18:31:32 +0200 Subject: [PATCH 016/138] Run php-cs-fixer --- Tests/Authentication/AuthenticationTrustResolverTest.php | 2 +- Tests/Validator/Constraints/UserPasswordTest.php | 2 +- Tests/Validator/Constraints/UserPasswordValidatorTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index 2ed0aa30..de694be2 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -16,8 +16,8 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Core\User\UserInterface; class AuthenticationTrustResolverTest extends TestCase { diff --git a/Tests/Validator/Constraints/UserPasswordTest.php b/Tests/Validator/Constraints/UserPasswordTest.php index 49a836db..95763021 100644 --- a/Tests/Validator/Constraints/UserPasswordTest.php +++ b/Tests/Validator/Constraints/UserPasswordTest.php @@ -37,7 +37,7 @@ public function provideServiceValidatedConstraints(): iterable { yield 'Doctrine style' => [new UserPassword(['service' => 'my_service'])]; - yield 'named arguments' => [new UserPassword(service: "my_service")]; + yield 'named arguments' => [new UserPassword(service: 'my_service')]; $metadata = new ClassMetadata(UserPasswordDummy::class); self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata)); diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 4d5009b4..3ca0e516 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -94,7 +94,7 @@ public function provideConstraints(): iterable { yield 'Doctrine style' => [new UserPassword(['message' => 'myMessage'])]; - yield 'named arguments' => [new UserPassword(message: "myMessage")]; + yield 'named arguments' => [new UserPassword(message: 'myMessage')]; } /** From 9c136ffd8a69a348275bc3b9037f630f63a4ba75 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Fri, 4 Jun 2021 20:15:48 +0200 Subject: [PATCH 017/138] [Security] Remove everything related to the deprecated authentication manager --- .../AuthenticationManagerInterface.php | 35 -- .../AuthenticationProviderManager.php | 133 ------- .../AuthenticationTrustResolver.php | 25 +- .../AuthenticationTrustResolverInterface.php | 12 - .../AnonymousAuthenticationProvider.php | 69 ---- .../AuthenticationProviderInterface.php | 44 --- .../Provider/DaoAuthenticationProvider.php | 121 ------ .../LdapBindAuthenticationProvider.php | 121 ------ ...PreAuthenticatedAuthenticationProvider.php | 86 ----- .../RememberMeAuthenticationProvider.php | 79 ---- .../Provider/UserAuthenticationProvider.php | 131 ------- Authentication/RememberMe/PersistentToken.php | 10 - .../RememberMe/PersistentTokenInterface.php | 8 +- Authentication/Token/AbstractToken.php | 142 +------ Authentication/Token/AnonymousToken.php | 78 ---- Authentication/Token/NullToken.php | 34 +- .../Token/PreAuthenticatedToken.php | 58 +-- Authentication/Token/RememberMeToken.php | 41 +- Authentication/Token/Storage/TokenStorage.php | 5 - Authentication/Token/SwitchUserToken.php | 19 +- Authentication/Token/TokenInterface.php | 34 +- .../Token/UsernamePasswordToken.php | 68 +--- AuthenticationEvents.php | 11 - Authorization/AccessDecisionManager.php | 8 +- Authorization/AuthorizationChecker.php | 40 +- Authorization/ExpressionLanguageProvider.php | 14 +- Authorization/Voter/AuthenticatedVoter.php | 31 +- Authorization/Voter/RoleVoter.php | 4 - Event/AuthenticationFailureEvent.php | 42 --- Exception/UserNotFoundException.php | 22 -- Exception/UsernameNotFoundException.php | 25 -- Security.php | 13 +- Signature/SignatureHasher.php | 2 +- .../AuthenticationProviderManagerTest.php | 210 ----------- .../AuthenticationTrustResolverTest.php | 92 ----- .../AnonymousAuthenticationProviderTest.php | 74 ---- .../DaoAuthenticationProviderTest.php | 356 ------------------ .../LdapBindAuthenticationProviderTest.php | 233 ------------ ...uthenticatedAuthenticationProviderTest.php | 140 ------- .../RememberMeAuthenticationProviderTest.php | 128 ------- .../UserAuthenticationProviderTest.php | 254 ------------- .../RememberMe/PersistentTokenTest.php | 14 - .../Token/AbstractTokenTest.php | 145 ------- .../Token/AnonymousTokenTest.php | 51 --- .../Token/PreAuthenticatedTokenTest.php | 38 -- .../Token/RememberMeTokenTest.php | 10 - .../Storage/UsageTrackingTokenStorageTest.php | 21 -- .../Token/SwitchUserTokenTest.php | 82 ---- .../Token/UsernamePasswordTokenTest.php | 55 +-- .../AccessDecisionManagerTest.php | 22 -- .../AuthorizationCheckerTest.php | 64 +--- .../Authorization/ExpressionLanguageTest.php | 34 +- .../Voter/AuthenticatedVoterTest.php | 33 -- Tests/Authorization/Voter/RoleVoterTest.php | 11 - ...UserMessageAuthenticationExceptionTest.php | 4 +- Tests/Exception/UserNotFoundExceptionTest.php | 22 -- Tests/SecurityTest.php | 11 - Tests/User/InMemoryUserProviderTest.php | 16 - Tests/User/InMemoryUserTest.php | 23 +- Tests/User/UserCheckerTest.php | 68 ---- Tests/User/UserTest.php | 152 -------- User/ChainUserProvider.php | 14 +- User/InMemoryUser.php | 98 +++-- User/InMemoryUserChecker.php | 33 +- User/InMemoryUserProvider.php | 43 +-- User/User.php | 218 ----------- User/UserChecker.php | 29 -- User/UserInterface.php | 8 +- User/UserProviderInterface.php | 11 +- 69 files changed, 132 insertions(+), 4250 deletions(-) delete mode 100644 Authentication/AuthenticationManagerInterface.php delete mode 100644 Authentication/AuthenticationProviderManager.php delete mode 100644 Authentication/Provider/AnonymousAuthenticationProvider.php delete mode 100644 Authentication/Provider/AuthenticationProviderInterface.php delete mode 100644 Authentication/Provider/DaoAuthenticationProvider.php delete mode 100644 Authentication/Provider/LdapBindAuthenticationProvider.php delete mode 100644 Authentication/Provider/PreAuthenticatedAuthenticationProvider.php delete mode 100644 Authentication/Provider/RememberMeAuthenticationProvider.php delete mode 100644 Authentication/Provider/UserAuthenticationProvider.php delete mode 100644 Authentication/Token/AnonymousToken.php delete mode 100644 Event/AuthenticationFailureEvent.php delete mode 100644 Exception/UsernameNotFoundException.php delete mode 100644 Tests/Authentication/AuthenticationProviderManagerTest.php delete mode 100644 Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php delete mode 100644 Tests/Authentication/Provider/DaoAuthenticationProviderTest.php delete mode 100644 Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php delete mode 100644 Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php delete mode 100644 Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php delete mode 100644 Tests/Authentication/Provider/UserAuthenticationProviderTest.php delete mode 100644 Tests/Authentication/Token/AnonymousTokenTest.php delete mode 100644 Tests/User/UserCheckerTest.php delete mode 100644 Tests/User/UserTest.php delete mode 100644 User/User.php delete mode 100644 User/UserChecker.php diff --git a/Authentication/AuthenticationManagerInterface.php b/Authentication/AuthenticationManagerInterface.php deleted file mode 100644 index bb908fd3..00000000 --- a/Authentication/AuthenticationManagerInterface.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication; - -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; - -/** - * AuthenticationManagerInterface is the interface for authentication managers, - * which process Token authentication. - * - * @author Fabien Potencier - * - * @internal - */ -interface AuthenticationManagerInterface -{ - /** - * Attempts to authenticate a TokenInterface object. - * - * @return TokenInterface - * - * @throws AuthenticationException if the authentication fails - */ - public function authenticate(TokenInterface $token); -} diff --git a/Authentication/AuthenticationProviderManager.php b/Authentication/AuthenticationProviderManager.php deleted file mode 100644 index 76be0f4d..00000000 --- a/Authentication/AuthenticationProviderManager.php +++ /dev/null @@ -1,133 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication; - -use Symfony\Component\PasswordHasher\Exception\InvalidPasswordException; -use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\AuthenticationEvents; -use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent; -use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; -use Symfony\Component\Security\Core\Exception\AccountStatusException; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', AuthenticationProviderManager::class); - -// Help opcache.preload discover always-needed symbols -class_exists(AuthenticationEvents::class); -class_exists(AuthenticationFailureEvent::class); -class_exists(AuthenticationSuccessEvent::class); - -/** - * AuthenticationProviderManager uses a list of AuthenticationProviderInterface - * instances to authenticate a Token. - * - * @author Fabien Potencier - * @author Johannes M. Schmitt - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -class AuthenticationProviderManager implements AuthenticationManagerInterface -{ - private $providers; - private $eraseCredentials; - private $eventDispatcher; - - /** - * @param iterable|AuthenticationProviderInterface[] $providers An iterable with AuthenticationProviderInterface instances as values - * @param bool $eraseCredentials Whether to erase credentials after authentication or not - * - * @throws \InvalidArgumentException - */ - public function __construct(iterable $providers, bool $eraseCredentials = true) - { - if (!$providers) { - throw new \InvalidArgumentException('You must at least add one authentication provider.'); - } - - $this->providers = $providers; - $this->eraseCredentials = $eraseCredentials; - } - - public function setEventDispatcher(EventDispatcherInterface $dispatcher) - { - $this->eventDispatcher = $dispatcher; - } - - /** - * {@inheritdoc} - */ - public function authenticate(TokenInterface $token) - { - $lastException = null; - $result = null; - - foreach ($this->providers as $provider) { - if (!$provider instanceof AuthenticationProviderInterface) { - throw new \InvalidArgumentException(sprintf('Provider "%s" must implement the AuthenticationProviderInterface.', get_debug_type($provider))); - } - - if (!$provider->supports($token)) { - continue; - } - - try { - $result = $provider->authenticate($token); - - if (null !== $result) { - break; - } - } catch (AccountStatusException $e) { - $lastException = $e; - - break; - } catch (AuthenticationException $e) { - $lastException = $e; - } catch (InvalidPasswordException $e) { - $lastException = new BadCredentialsException('Bad credentials.', 0, $e); - } - } - - if (null !== $result) { - if (true === $this->eraseCredentials) { - $result->eraseCredentials(); - } - - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($result), AuthenticationEvents::AUTHENTICATION_SUCCESS); - } - - // @deprecated since Symfony 5.3 - if ($user = $result->getUser() instanceof UserInterface && !method_exists($result->getUser(), 'getUserIdentifier')) { - trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier(): string" in user class "%s" is deprecated. This method will replace "getUsername()" in Symfony 6.0.', get_debug_type($result->getUser())); - } - - return $result; - } - - if (null === $lastException) { - $lastException = new ProviderNotFoundException(sprintf('No Authentication Provider found for token of class "%s".', \get_class($token))); - } - - if (null !== $this->eventDispatcher) { - $this->eventDispatcher->dispatch(new AuthenticationFailureEvent($token, $lastException), AuthenticationEvents::AUTHENTICATION_FAILURE); - } - - $lastException->setToken($token); - - throw $lastException; - } -} diff --git a/Authentication/AuthenticationTrustResolver.php b/Authentication/AuthenticationTrustResolver.php index c8fa3f54..03366886 100644 --- a/Authentication/AuthenticationTrustResolver.php +++ b/Authentication/AuthenticationTrustResolver.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Core\Authentication; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -25,25 +24,7 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac { public function isAuthenticated(TokenInterface $token = null): bool { - return null !== $token && !$token instanceof NullToken - // @deprecated since Symfony 5.4, TokenInterface::isAuthenticated() and AnonymousToken no longer exists in 6.0 - && !$token instanceof AnonymousToken && $token->isAuthenticated(false); - } - - /** - * {@inheritdoc} - */ - public function isAnonymous(TokenInterface $token = null/*, $deprecation = true*/) - { - if (1 === \func_num_args() || false !== func_get_arg(1)) { - trigger_deprecation('symfony/security-core', '5.4', 'The "%s()" method is deprecated, use "isAuthenticated()" or "isFullFledged()" if you want to check if the request is (fully) authenticated.', __METHOD__); - } - - if (null === $token) { - return false; - } - - return $token instanceof AnonymousToken || $token instanceof NullToken; + return null !== $token && !$token instanceof NullToken; } /** @@ -63,10 +44,10 @@ public function isRememberMe(TokenInterface $token = null) */ public function isFullFledged(TokenInterface $token = null) { - if (null === $token) { + if (null === $token || $token instanceof NullToken) { return false; } - return !$this->isAnonymous($token, false) && !$this->isRememberMe($token); + return !$this->isRememberMe($token); } } diff --git a/Authentication/AuthenticationTrustResolverInterface.php b/Authentication/AuthenticationTrustResolverInterface.php index 1122ffef..16723d88 100644 --- a/Authentication/AuthenticationTrustResolverInterface.php +++ b/Authentication/AuthenticationTrustResolverInterface.php @@ -22,18 +22,6 @@ */ interface AuthenticationTrustResolverInterface { - /** - * Resolves whether the passed token implementation is authenticated - * anonymously. - * - * If null is passed, the method must return false. - * - * @return bool - * - * @deprecated since Symfony 5.4, use !isAuthenticated() instead - */ - public function isAnonymous(TokenInterface $token = null); - /** * Resolves whether the passed token implementation is authenticated * using remember-me capabilities. diff --git a/Authentication/Provider/AnonymousAuthenticationProvider.php b/Authentication/Provider/AnonymousAuthenticationProvider.php deleted file mode 100644 index 53f8cf18..00000000 --- a/Authentication/Provider/AnonymousAuthenticationProvider.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', AnonymousAuthenticationProvider::class); - -/** - * AnonymousAuthenticationProvider validates AnonymousToken instances. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -class AnonymousAuthenticationProvider implements AuthenticationProviderInterface -{ - /** - * Used to determine if the token is created by the application - * instead of a malicious client. - * - * @var string - */ - private $secret; - - /** - * @param string $secret The secret shared with the AnonymousToken - */ - public function __construct(string $secret) - { - $this->secret = $secret; - } - - /** - * {@inheritdoc} - */ - public function authenticate(TokenInterface $token) - { - if (!$this->supports($token)) { - throw new AuthenticationException('The token is not supported by this authentication provider.'); - } - - if ($this->secret !== $token->getSecret()) { - throw new BadCredentialsException('The Token does not contain the expected key.'); - } - - return $token; - } - - /** - * {@inheritdoc} - */ - public function supports(TokenInterface $token) - { - return $token instanceof AnonymousToken; - } -} diff --git a/Authentication/Provider/AuthenticationProviderInterface.php b/Authentication/Provider/AuthenticationProviderInterface.php deleted file mode 100644 index fb57ed80..00000000 --- a/Authentication/Provider/AuthenticationProviderInterface.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" interface is deprecated, use the new authenticator system instead.', AuthenticationProviderInterface::class); - -/** - * AuthenticationProviderInterface is the interface for all authentication - * providers. - * - * Concrete implementations processes specific Token instances. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -interface AuthenticationProviderInterface extends AuthenticationManagerInterface -{ - /** - * Use this constant for not provided username. - * - * @var string - */ - public const USERNAME_NONE_PROVIDED = 'NONE_PROVIDED'; - - /** - * Checks whether this provider supports the given token. - * - * @return bool - */ - public function supports(TokenInterface $token); -} diff --git a/Authentication/Provider/DaoAuthenticationProvider.php b/Authentication/Provider/DaoAuthenticationProvider.php deleted file mode 100644 index c9286b3f..00000000 --- a/Authentication/Provider/DaoAuthenticationProvider.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\InvalidArgumentException; -use Symfony\Component\Security\Core\Exception\UserNotFoundException; -use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', DaoAuthenticationProvider::class); - -/** - * DaoAuthenticationProvider uses a UserProviderInterface to retrieve the user - * for a UsernamePasswordToken. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -class DaoAuthenticationProvider extends UserAuthenticationProvider -{ - private $hasherFactory; - private $userProvider; - - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, PasswordHasherFactoryInterface $hasherFactory, bool $hideUserNotFoundExceptions = true) - { - parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); - - $this->hasherFactory = $hasherFactory; - $this->userProvider = $userProvider; - } - - /** - * {@inheritdoc} - */ - protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) - { - if (!$user instanceof PasswordAuthenticatedUserInterface) { - throw new InvalidArgumentException(sprintf('Class "%s" must implement "%s" for using password-based authentication.', get_debug_type($user), PasswordAuthenticatedUserInterface::class)); - } - - $currentUser = $token->getUser(); - - if ($currentUser instanceof PasswordAuthenticatedUserInterface) { - if ($currentUser->getPassword() !== $user->getPassword()) { - throw new BadCredentialsException('The credentials were changed from another session.'); - } - } else { - if ('' === ($presentedPassword = $token->getCredentials())) { - throw new BadCredentialsException('The presented password cannot be empty.'); - } - - if (null === $user->getPassword()) { - throw new BadCredentialsException('The presented password is invalid.'); - } - - $salt = $user instanceof LegacyPasswordAuthenticatedUserInterface ? $user->getSalt() : null; - $hasher = $this->hasherFactory->getPasswordHasher($user); - - if (!$hasher->verify($user->getPassword(), $presentedPassword, $salt)) { - throw new BadCredentialsException('The presented password is invalid.'); - } - - if ($this->userProvider instanceof PasswordUpgraderInterface && $hasher->needsRehash($user->getPassword())) { - $this->userProvider->upgradePassword($user, $hasher->hash($presentedPassword, $salt)); - } - } - } - - /** - * {@inheritdoc} - */ - protected function retrieveUser(string $userIdentifier, UsernamePasswordToken $token) - { - $user = $token->getUser(); - if ($user instanceof UserInterface) { - return $user; - } - - try { - // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 - if (method_exists($this->userProvider, 'loadUserByIdentifier')) { - $user = $this->userProvider->loadUserByIdentifier($userIdentifier); - } else { - trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); - - $user = $this->userProvider->loadUserByUsername($userIdentifier); - } - - if (!$user instanceof UserInterface) { - throw new AuthenticationServiceException('The user provider must return a UserInterface object.'); - } - - return $user; - } catch (UserNotFoundException $e) { - $e->setUserIdentifier($userIdentifier); - throw $e; - } catch (\Exception $e) { - $e = new AuthenticationServiceException($e->getMessage(), 0, $e); - $e->setToken($token); - throw $e; - } - } -} diff --git a/Authentication/Provider/LdapBindAuthenticationProvider.php b/Authentication/Provider/LdapBindAuthenticationProvider.php deleted file mode 100644 index 7de715d7..00000000 --- a/Authentication/Provider/LdapBindAuthenticationProvider.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\Ldap\Exception\ConnectionException; -use Symfony\Component\Ldap\LdapInterface; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\LogicException; -use Symfony\Component\Security\Core\Exception\UserNotFoundException; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', LdapBindAuthenticationProvider::class); - -/** - * LdapBindAuthenticationProvider authenticates a user against an LDAP server. - * - * The only way to check user credentials is to try to connect the user with its - * credentials to the ldap. - * - * @author Charles Sarrazin - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -class LdapBindAuthenticationProvider extends UserAuthenticationProvider -{ - private $userProvider; - private $ldap; - private $dnString; - private $queryString; - private $searchDn; - private $searchPassword; - - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, LdapInterface $ldap, string $dnString = '{user_identifier}', bool $hideUserNotFoundExceptions = true, string $searchDn = '', string $searchPassword = '') - { - parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); - - $this->userProvider = $userProvider; - $this->ldap = $ldap; - $this->dnString = $dnString; - $this->searchDn = $searchDn; - $this->searchPassword = $searchPassword; - } - - /** - * Set a query string to use in order to find a DN for the user identifier. - */ - public function setQueryString(string $queryString) - { - $this->queryString = $queryString; - } - - /** - * {@inheritdoc} - */ - protected function retrieveUser(string $userIdentifier, UsernamePasswordToken $token) - { - if (AuthenticationProviderInterface::USERNAME_NONE_PROVIDED === $userIdentifier) { - throw new UserNotFoundException('User identifier can not be null.'); - } - - // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 - if (method_exists($this->userProvider, 'loadUserByIdentifier')) { - return $this->userProvider->loadUserByIdentifier($userIdentifier); - } else { - trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); - - return $this->userProvider->loadUserByUsername($userIdentifier); - } - } - - /** - * {@inheritdoc} - */ - protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) - { - // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 - $userIdentifier = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); - $password = $token->getCredentials(); - - if ('' === (string) $password) { - throw new BadCredentialsException('The presented password must not be empty.'); - } - - try { - if ($this->queryString) { - if ('' !== $this->searchDn && '' !== $this->searchPassword) { - $this->ldap->bind($this->searchDn, $this->searchPassword); - } else { - throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.'); - } - $userIdentifier = $this->ldap->escape($userIdentifier, '', LdapInterface::ESCAPE_FILTER); - $query = str_replace(['{username}', '{user_identifier}'], $userIdentifier, $this->queryString); - $result = $this->ldap->query($this->dnString, $query)->execute(); - if (1 !== $result->count()) { - throw new BadCredentialsException('The presented username is invalid.'); - } - - $dn = $result[0]->getDn(); - } else { - $userIdentifier = $this->ldap->escape($userIdentifier, '', LdapInterface::ESCAPE_DN); - $dn = str_replace(['{username}', '{user_identifier}'], $userIdentifier, $this->dnString); - } - - $this->ldap->bind($dn, $password); - } catch (ConnectionException $e) { - throw new BadCredentialsException('The presented password is invalid.'); - } - } -} diff --git a/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php deleted file mode 100644 index d81e31bb..00000000 --- a/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', PreAuthenticatedAuthenticationProvider::class); - -/** - * Processes a pre-authenticated authentication request. - * - * This authentication provider will not perform any checks on authentication - * requests, as they should already be pre-authenticated. However, the - * UserProviderInterface implementation may still throw a - * UserNotFoundException, for example. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderInterface -{ - private $userProvider; - private $userChecker; - private $providerKey; - - public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey) - { - $this->userProvider = $userProvider; - $this->userChecker = $userChecker; - $this->providerKey = $providerKey; - } - - /** - * {@inheritdoc} - */ - public function authenticate(TokenInterface $token) - { - if (!$this->supports($token)) { - throw new AuthenticationException('The token is not supported by this authentication provider.'); - } - - if (!$user = $token->getUser()) { - throw new BadCredentialsException('No pre-authenticated principal found in request.'); - } - - $userIdentifier = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); - // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 - if (method_exists($this->userProvider, 'loadUserByIdentifier')) { - $user = $this->userProvider->loadUserByIdentifier($userIdentifier); - } else { - trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); - - $user = $this->userProvider->loadUserByUsername($userIdentifier); - } - - $this->userChecker->checkPostAuth($user); - - $authenticatedToken = new PreAuthenticatedToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles()); - $authenticatedToken->setAttributes($token->getAttributes()); - - return $authenticatedToken; - } - - /** - * {@inheritdoc} - */ - public function supports(TokenInterface $token) - { - return $token instanceof PreAuthenticatedToken && $this->providerKey === $token->getFirewallName(); - } -} diff --git a/Authentication/Provider/RememberMeAuthenticationProvider.php b/Authentication/Provider/RememberMeAuthenticationProvider.php deleted file mode 100644 index 2fd52f2d..00000000 --- a/Authentication/Provider/RememberMeAuthenticationProvider.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\LogicException; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', RememberMeAuthenticationProvider::class); - -/** - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -class RememberMeAuthenticationProvider implements AuthenticationProviderInterface -{ - private $userChecker; - private $secret; - private $providerKey; - - /** - * @param string $secret A secret - * @param string $providerKey A provider secret - */ - public function __construct(UserCheckerInterface $userChecker, string $secret, string $providerKey) - { - $this->userChecker = $userChecker; - $this->secret = $secret; - $this->providerKey = $providerKey; - } - - /** - * {@inheritdoc} - */ - public function authenticate(TokenInterface $token) - { - if (!$this->supports($token)) { - throw new AuthenticationException('The token is not supported by this authentication provider.'); - } - - if ($this->secret !== $token->getSecret()) { - throw new BadCredentialsException('The presented secret does not match.'); - } - - $user = $token->getUser(); - - if (!$user instanceof UserInterface) { - throw new LogicException(sprintf('Method "%s::getUser()" must return a "%s" instance, "%s" returned.', get_debug_type($token), UserInterface::class, get_debug_type($user))); - } - - $this->userChecker->checkPreAuth($user); - $this->userChecker->checkPostAuth($user); - - $authenticatedToken = new RememberMeToken($user, $this->providerKey, $this->secret); - $authenticatedToken->setAttributes($token->getAttributes()); - - return $authenticatedToken; - } - - /** - * {@inheritdoc} - */ - public function supports(TokenInterface $token) - { - return $token instanceof RememberMeToken && $token->getFirewallName() === $this->providerKey; - } -} diff --git a/Authentication/Provider/UserAuthenticationProvider.php b/Authentication/Provider/UserAuthenticationProvider.php deleted file mode 100644 index aa123ac9..00000000 --- a/Authentication/Provider/UserAuthenticationProvider.php +++ /dev/null @@ -1,131 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Provider; - -use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\AccountStatusException; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; -use Symfony\Component\Security\Core\Exception\UserNotFoundException; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', UserAuthenticationProvider::class); - -/** - * UserProviderInterface retrieves users for UsernamePasswordToken tokens. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use the new authenticator system instead - */ -abstract class UserAuthenticationProvider implements AuthenticationProviderInterface -{ - private $hideUserNotFoundExceptions; - private $userChecker; - private $providerKey; - - /** - * @throws \InvalidArgumentException - */ - public function __construct(UserCheckerInterface $userChecker, string $providerKey, bool $hideUserNotFoundExceptions = true) - { - if (empty($providerKey)) { - throw new \InvalidArgumentException('$providerKey must not be empty.'); - } - - $this->userChecker = $userChecker; - $this->providerKey = $providerKey; - $this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions; - } - - /** - * {@inheritdoc} - */ - public function authenticate(TokenInterface $token) - { - if (!$this->supports($token)) { - throw new AuthenticationException('The token is not supported by this authentication provider.'); - } - - $username = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); - if ('' === $username || null === $username) { - $username = AuthenticationProviderInterface::USERNAME_NONE_PROVIDED; - } - - try { - $user = $this->retrieveUser($username, $token); - } catch (UserNotFoundException $e) { - if ($this->hideUserNotFoundExceptions) { - throw new BadCredentialsException('Bad credentials.', 0, $e); - } - $e->setUserIdentifier($username); - - throw $e; - } - - if (!$user instanceof UserInterface) { - throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.'); - } - - try { - $this->userChecker->checkPreAuth($user); - $this->checkAuthentication($user, $token); - $this->userChecker->checkPostAuth($user); - } catch (AccountStatusException | BadCredentialsException $e) { - if ($this->hideUserNotFoundExceptions && !$e instanceof CustomUserMessageAccountStatusException) { - throw new BadCredentialsException('Bad credentials.', 0, $e); - } - - throw $e; - } - - if ($token instanceof SwitchUserToken) { - $authenticatedToken = new SwitchUserToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles(), $token->getOriginalToken()); - } else { - $authenticatedToken = new UsernamePasswordToken($user, $token->getCredentials(), $this->providerKey, $user->getRoles()); - } - - $authenticatedToken->setAttributes($token->getAttributes()); - - return $authenticatedToken; - } - - /** - * {@inheritdoc} - */ - public function supports(TokenInterface $token) - { - return $token instanceof UsernamePasswordToken && $this->providerKey === $token->getFirewallName(); - } - - /** - * Retrieves the user from an implementation-specific location. - * - * @return UserInterface - * - * @throws AuthenticationException if the credentials could not be validated - */ - abstract protected function retrieveUser(string $username, UsernamePasswordToken $token); - - /** - * Does additional checks on the user and token (like validating the - * credentials). - * - * @throws AuthenticationException if the credentials could not be validated - */ - abstract protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token); -} diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index b8337adf..42d22bea 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -54,16 +54,6 @@ public function getClass(): string return $this->class; } - /** - * {@inheritdoc} - */ - public function getUsername(): string - { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); - - return $this->userIdentifier; - } - public function getUserIdentifier(): string { return $this->userIdentifier; diff --git a/Authentication/RememberMe/PersistentTokenInterface.php b/Authentication/RememberMe/PersistentTokenInterface.php index 18eea225..cf73b0a0 100644 --- a/Authentication/RememberMe/PersistentTokenInterface.php +++ b/Authentication/RememberMe/PersistentTokenInterface.php @@ -15,8 +15,6 @@ * Interface to be implemented by persistent token classes (such as * Doctrine entities representing a remember-me token). * - * @method string getUserIdentifier() returns the identifier used to authenticate (e.g. their e-mailaddress or username) - * * @author Johannes M. Schmitt */ interface PersistentTokenInterface @@ -50,9 +48,7 @@ public function getTokenValue(); public function getLastUsed(); /** - * @return string - * - * @deprecated since Symfony 5.3, use getUserIdentifier() instead + * Returns the identifier used to authenticate (e.g. their e-mailaddress or username). */ - public function getUsername(); + public function getUserIdentifier(): string; } diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index e4d097fc..4220ebaf 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -26,7 +26,6 @@ abstract class AbstractToken implements TokenInterface, \Serializable { private $user; private $roleNames = []; - private $authenticated = false; private $attributes = []; /** @@ -49,38 +48,9 @@ public function getRoleNames(): array return $this->roleNames; } - /** - * {@inheritdoc} - */ - public function getUsername(/* $legacy = true */) - { - if (1 === \func_num_args() && false === func_get_arg(0)) { - return null; - } - - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); - - if ($this->user instanceof UserInterface) { - return method_exists($this->user, 'getUserIdentifier') ? $this->user->getUserIdentifier() : $this->user->getUsername(); - } - - return (string) $this->user; - } - public function getUserIdentifier(): string { - // method returns "null" in non-legacy mode if not overridden - $username = $this->getUsername(false); - if (null !== $username) { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s::getUsername()" is deprecated, override "getUserIdentifier()" instead.', get_debug_type($this)); - } - - if ($this->user instanceof UserInterface) { - // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 - return method_exists($this->user, 'getUserIdentifier') ? $this->user->getUserIdentifier() : $this->user->getUsername(); - } - - return (string) $this->user; + return $this->user->getUserIdentifier(); } /** @@ -94,65 +64,11 @@ public function getUser() /** * {@inheritdoc} */ - public function setUser(string|\Stringable|UserInterface $user) + public function setUser(UserInterface $user) { - if (!$user instanceof UserInterface) { - trigger_deprecation('symfony/security-core', '5.4', 'Using an object that is not an instance of "%s" as $user in "%s" is deprecated.', UserInterface::class, static::class); - } - - // @deprecated since Symfony 5.4, remove the whole block if/elseif/else block in 6.0 - if (1 < \func_num_args() && !func_get_arg(1)) { - // ContextListener checks if the user has changed on its own and calls `setAuthenticated()` subsequently, - // avoid doing the same checks twice - $changed = false; - } elseif (null === $this->user) { - $changed = false; - } elseif ($this->user instanceof UserInterface) { - if (!$user instanceof UserInterface) { - $changed = true; - } else { - $changed = $this->hasUserChanged($user); - } - } elseif ($user instanceof UserInterface) { - $changed = true; - } else { - $changed = (string) $this->user !== (string) $user; - } - - // @deprecated since Symfony 5.4 - if ($changed) { - $this->setAuthenticated(false, false); - } - $this->user = $user; } - /** - * {@inheritdoc} - * - * @deprecated since Symfony 5.4 - */ - public function isAuthenticated() - { - if (1 > \func_num_args() || func_get_arg(0)) { - trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated. In version 6.0, security tokens won\'t have an "authenticated" flag anymore and will always be considered authenticated.', __METHOD__); - } - - return $this->authenticated; - } - - /** - * {@inheritdoc} - */ - public function setAuthenticated(bool $authenticated) - { - if (2 > \func_num_args() || func_get_arg(1)) { - trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated. In version 6.0, security tokens won\'t have an "authenticated" state anymore and will always be considered as authenticated.', __METHOD__); - } - - $this->authenticated = $authenticated; - } - /** * {@inheritdoc} */ @@ -180,7 +96,7 @@ public function eraseCredentials() */ public function __serialize(): array { - return [$this->user, $this->authenticated, null, $this->attributes, $this->roleNames]; + return [$this->user, true, null, $this->attributes, $this->roleNames]; } /** @@ -201,7 +117,7 @@ public function __serialize(): array */ public function __unserialize(array $data): void { - [$this->user, $this->authenticated, , $this->attributes, $this->roleNames] = $data; + [$this->user, , , $this->attributes, $this->roleNames] = $data; } /** @@ -265,7 +181,7 @@ public function __toString(): string $roles[] = $role; } - return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUserIdentifier(), json_encode($this->authenticated), implode(', ', $roles)); + return sprintf('%s(user="%s", roles="%s")', $class, $this->getUserIdentifier(), implode(', ', $roles)); } /** @@ -283,52 +199,4 @@ final public function unserialize(string $serialized) { $this->__unserialize(unserialize($serialized)); } - - /** - * @deprecated since Symfony 5.4 - */ - private function hasUserChanged(UserInterface $user): bool - { - if (!($this->user instanceof UserInterface)) { - throw new \BadMethodCallException('Method "hasUserChanged" should be called when current user class is instance of "UserInterface".'); - } - - if ($this->user instanceof EquatableInterface) { - return !(bool) $this->user->isEqualTo($user); - } - - if ($this->user instanceof PasswordAuthenticatedUserInterface || $user instanceof PasswordAuthenticatedUserInterface) { - if (!$this->user instanceof PasswordAuthenticatedUserInterface || !$user instanceof PasswordAuthenticatedUserInterface || $this->user->getPassword() !== $user->getPassword()) { - return true; - } - - if ($this->user instanceof LegacyPasswordAuthenticatedUserInterface xor $user instanceof LegacyPasswordAuthenticatedUserInterface) { - return true; - } - - if ($this->user instanceof LegacyPasswordAuthenticatedUserInterface && $user instanceof LegacyPasswordAuthenticatedUserInterface && $this->user->getSalt() !== $user->getSalt()) { - return true; - } - } - - $userRoles = array_map('strval', (array) $user->getRoles()); - - if ($this instanceof SwitchUserToken) { - $userRoles[] = 'ROLE_PREVIOUS_ADMIN'; - } - - if (\count($userRoles) !== \count($this->getRoleNames()) || \count($userRoles) !== \count(array_intersect($userRoles, $this->getRoleNames()))) { - return true; - } - - // @deprecated since Symfony 5.3, drop getUsername() in 6.0 - $userIdentifier = function ($user) { - return method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(); - }; - if ($userIdentifier($this->user) !== $userIdentifier($user)) { - return true; - } - - return false; - } } diff --git a/Authentication/Token/AnonymousToken.php b/Authentication/Token/AnonymousToken.php deleted file mode 100644 index 85316dba..00000000 --- a/Authentication/Token/AnonymousToken.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Authentication\Token; - -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * AnonymousToken represents an anonymous token. - * - * @author Fabien Potencier - * - * @deprecated since 5.4, anonymous is now represented by the absence of a token - */ -class AnonymousToken extends AbstractToken -{ - private $secret; - - /** - * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client - * @param string[] $roles - */ - public function __construct(string $secret, string|\Stringable|UserInterface $user, array $roles = []) - { - trigger_deprecation('symfony/security-core', '5.4', 'The "%s" class is deprecated.', __CLASS__); - - parent::__construct($roles); - - $this->secret = $secret; - $this->setUser($user); - // @deprecated since Symfony 5.4 - $this->setAuthenticated(true, false); - } - - /** - * {@inheritdoc} - */ - public function getCredentials() - { - return ''; - } - - /** - * Returns the secret. - * - * @return string - */ - public function getSecret() - { - return $this->secret; - } - - /** - * {@inheritdoc} - */ - public function __serialize(): array - { - return [$this->secret, parent::__serialize()]; - } - - /** - * {@inheritdoc} - */ - public function __unserialize(array $data): void - { - [$this->secret, $parentData] = $data; - $parentData = \is_array($parentData) ? $parentData : unserialize($parentData); - parent::__unserialize($parentData); - } -} diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index 4bfe0e9d..4a2a7e13 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -28,53 +28,21 @@ public function getRoleNames(): array return []; } - public function getCredentials() - { - return ''; - } - public function getUser() { return ''; } - public function setUser(string|\Stringable|UserInterface $user) + public function setUser(UserInterface $user) { throw new \BadMethodCallException('Cannot set user on a NullToken.'); } - public function getUsername() - { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); - - return ''; - } - public function getUserIdentifier(): string { return ''; } - /** - * @deprecated since Symfony 5.4 - */ - public function isAuthenticated() - { - if (0 === \func_num_args() || func_get_arg(0)) { - trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated. In version 6.0, security tokens won\'t have an "authenticated" flag anymore and will always be considered authenticated.', __METHOD__); - } - - return true; - } - - /** - * @deprecated since Symfony 5.4 - */ - public function setAuthenticated(bool $isAuthenticated) - { - throw new \BadMethodCallException('Cannot change authentication state of NullToken.'); - } - public function eraseCredentials() { } diff --git a/Authentication/Token/PreAuthenticatedToken.php b/Authentication/Token/PreAuthenticatedToken.php index 50159697..30579c52 100644 --- a/Authentication/Token/PreAuthenticatedToken.php +++ b/Authentication/Token/PreAuthenticatedToken.php @@ -20,22 +20,13 @@ */ class PreAuthenticatedToken extends AbstractToken { - private $credentials; private $firewallName; /** * @param string[] $roles */ - public function __construct(string|\Stringable|UserInterface $user, /*string*/ $firewallName, /*array*/ $roles = []) + public function __construct(UserInterface $user, string $firewallName, array $roles = []) { - if (\is_string($roles)) { - trigger_deprecation('symfony/security-core', '5.4', 'Argument $credentials of "%s()" is deprecated.', __METHOD__); - - $credentials = $firewallName; - $firewallName = $roles; - $roles = \func_num_args() > 3 ? func_get_arg(3) : []; - } - parent::__construct($roles); if ('' === $firewallName) { @@ -43,53 +34,12 @@ public function __construct(string|\Stringable|UserInterface $user, /*string*/ $ } $this->setUser($user); - $this->credentials = $credentials ?? null; $this->firewallName = $firewallName; - - if ($roles) { - $this->setAuthenticated(true, false); - } - } - - /** - * Returns the provider key. - * - * @return string The provider key - * - * @deprecated since Symfony 5.2, use getFirewallName() instead - */ - public function getProviderKey() - { - if (1 !== \func_num_args() || true !== func_get_arg(0)) { - trigger_deprecation('symfony/security-core', '5.2', 'Method "%s()" is deprecated, use "getFirewallName()" instead.', __METHOD__); - } - - return $this->firewallName; } public function getFirewallName(): string { - return $this->getProviderKey(true); - } - - /** - * {@inheritdoc} - */ - public function getCredentials() - { - trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated.', __METHOD__); - - return $this->credentials; - } - - /** - * {@inheritdoc} - */ - public function eraseCredentials() - { - parent::eraseCredentials(); - - $this->credentials = null; + return $this->firewallName; } /** @@ -97,7 +47,7 @@ public function eraseCredentials() */ public function __serialize(): array { - return [$this->credentials, $this->firewallName, parent::__serialize()]; + return [null, $this->firewallName, parent::__serialize()]; } /** @@ -105,7 +55,7 @@ public function __serialize(): array */ public function __unserialize(array $data): void { - [$this->credentials, $this->firewallName, $parentData] = $data; + [, $this->firewallName, $parentData] = $data; $parentData = \is_array($parentData) ? $parentData : unserialize($parentData); parent::__unserialize($parentData); } diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index 48319a8e..e2a9da9e 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -44,40 +44,11 @@ public function __construct(UserInterface $user, string $firewallName, string $s $this->secret = $secret; $this->setUser($user); - parent::setAuthenticated(true, false); - } - - /** - * {@inheritdoc} - */ - public function setAuthenticated(bool $authenticated) - { - if ($authenticated) { - throw new \LogicException('You cannot set this token to authenticated after creation.'); - } - - parent::setAuthenticated(false, false); - } - - /** - * Returns the provider secret. - * - * @return string The provider secret - * - * @deprecated since Symfony 5.2, use getFirewallName() instead - */ - public function getProviderKey() - { - if (1 !== \func_num_args() || true !== func_get_arg(0)) { - trigger_deprecation('symfony/security-core', '5.2', 'Method "%s()" is deprecated, use "getFirewallName()" instead.', __METHOD__); - } - - return $this->firewallName; } public function getFirewallName(): string { - return $this->getProviderKey(true); + return $this->firewallName; } /** @@ -90,16 +61,6 @@ public function getSecret() return $this->secret; } - /** - * {@inheritdoc} - */ - public function getCredentials() - { - trigger_deprecation('symfony/security-core', '5.4', 'Method "%s()" is deprecated.', __METHOD__); - - return ''; - } - /** * {@inheritdoc} */ diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index b4793244..850c05e7 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -48,11 +48,6 @@ public function setToken(TokenInterface $token = null) if ($token) { // ensure any initializer is called $this->getToken(); - - // @deprecated since Symfony 5.3 - if (!method_exists($token, 'getUserIdentifier')) { - trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier(): string" in token class "%s" is deprecated. This method will replace "getUsername()" in Symfony 6.0.', get_debug_type($token)); - } } $this->initializer = null; diff --git a/Authentication/Token/SwitchUserToken.php b/Authentication/Token/SwitchUserToken.php index d6687343..2c934f07 100644 --- a/Authentication/Token/SwitchUserToken.php +++ b/Authentication/Token/SwitchUserToken.php @@ -29,24 +29,9 @@ class SwitchUserToken extends UsernamePasswordToken * * @throws \InvalidArgumentException */ - public function __construct(string|\Stringable|UserInterface $user, /*string*/ $firewallName, /*array*/ $roles, /*TokenInterface*/ $originalToken, /*string*/ $originatedFromUri = null) + public function __construct(UserInterface $user, string $firewallName, array $roles, TokenInterface $originalToken, string $originatedFromUri = null) { - if (\is_string($roles)) { - // @deprecated since 5.4, deprecation is triggered by UsernamePasswordToken::__construct() - $credentials = $firewallName; - $firewallName = $roles; - $roles = $originalToken; - $originalToken = $originatedFromUri; - $originatedFromUri = \func_num_args() > 5 ? func_get_arg(5) : null; - - parent::__construct($user, $credentials, $firewallName, $roles); - } else { - parent::__construct($user, $firewallName, $roles); - } - - if (!$originalToken instanceof TokenInterface) { - throw new \TypeError(sprintf('Argument $originalToken of "%s" must be an instance of "%s", "%s" given.', __METHOD__, TokenInterface::class, get_debug_type($originalToken))); - } + parent::__construct($user, $firewallName, $roles); $this->originalToken = $originalToken; $this->originatedFromUri = $originatedFromUri; diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index f981068e..0faf770f 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -37,15 +37,6 @@ public function __toString(): string; */ public function getRoleNames(): array; - /** - * Returns the user credentials. - * - * @return mixed - * - * @deprecated since 5.4 - */ - public function getCredentials(); - /** * Returns a user representation. * @@ -60,23 +51,7 @@ public function getUser(); * * @throws \InvalidArgumentException */ - public function setUser(string|\Stringable|UserInterface $user); - - /** - * Returns whether the user is authenticated or not. - * - * @return bool true if the token has been authenticated, false otherwise - * - * @deprecated since Symfony 5.4. In 6.0, security tokens will always be considered authenticated - */ - public function isAuthenticated(); - - /** - * Sets the authenticated flag. - * - * @deprecated since Symfony 5.4. In 6.0, security tokens will always be considered authenticated - */ - public function setAuthenticated(bool $isAuthenticated); + public function setUser(UserInterface $user); /** * Removes sensitive information from the token. @@ -127,11 +102,4 @@ public function __serialize(): array; * Restores the object state from an array given by __serialize(). */ public function __unserialize(array $data): void; - - /** - * @return string - * - * @deprecated since Symfony 5.3, use getUserIdentifier() instead - */ - public function getUsername(); } diff --git a/Authentication/Token/UsernamePasswordToken.php b/Authentication/Token/UsernamePasswordToken.php index 97189f1f..6dae8ec9 100644 --- a/Authentication/Token/UsernamePasswordToken.php +++ b/Authentication/Token/UsernamePasswordToken.php @@ -20,19 +20,10 @@ */ class UsernamePasswordToken extends AbstractToken { - private $credentials; private $firewallName; - public function __construct(string|\Stringable|UserInterface $user, /*string*/ $firewallName, /*array*/ $roles = []) + public function __construct(UserInterface $user, string $firewallName, array $roles = []) { - if (\is_string($roles)) { - trigger_deprecation('symfony/security-core', '5.4', 'The $credentials argument of "%s" is deprecated.', static::class.'::__construct'); - - $credentials = $firewallName; - $firewallName = $roles; - $roles = \func_num_args() > 3 ? func_get_arg(3) : []; - } - parent::__construct($roles); if ('' === $firewallName) { @@ -40,63 +31,12 @@ public function __construct(string|\Stringable|UserInterface $user, /*string*/ $ } $this->setUser($user); - $this->credentials = $credentials ?? null; $this->firewallName = $firewallName; - - parent::setAuthenticated(\count($roles) > 0, false); - } - - /** - * {@inheritdoc} - */ - public function setAuthenticated(bool $isAuthenticated) - { - if ($isAuthenticated) { - throw new \LogicException('Cannot set this token to trusted after instantiation.'); - } - - parent::setAuthenticated(false, false); - } - - /** - * {@inheritdoc} - */ - public function getCredentials() - { - trigger_deprecation('symfony/security-core', '5.4', 'Method "%s" is deprecated.', __METHOD__); - - return $this->credentials; - } - - /** - * Returns the provider key. - * - * @return string The provider key - * - * @deprecated since Symfony 5.2, use getFirewallName() instead - */ - public function getProviderKey() - { - if (1 !== \func_num_args() || true !== func_get_arg(0)) { - trigger_deprecation('symfony/security-core', '5.2', 'Method "%s" is deprecated, use "getFirewallName()" instead.', __METHOD__); - } - - return $this->firewallName; } public function getFirewallName(): string { - return $this->getProviderKey(true); - } - - /** - * {@inheritdoc} - */ - public function eraseCredentials() - { - parent::eraseCredentials(); - - $this->credentials = null; + return $this->firewallName; } /** @@ -104,7 +44,7 @@ public function eraseCredentials() */ public function __serialize(): array { - return [$this->credentials, $this->firewallName, parent::__serialize()]; + return [null, $this->firewallName, parent::__serialize()]; } /** @@ -112,7 +52,7 @@ public function __serialize(): array */ public function __unserialize(array $data): void { - [$this->credentials, $this->firewallName, $parentData] = $data; + [, $this->firewallName, $parentData] = $data; $parentData = \is_array($parentData) ? $parentData : unserialize($parentData); parent::__unserialize($parentData); } diff --git a/AuthenticationEvents.php b/AuthenticationEvents.php index fc286d2a..30498d0a 100644 --- a/AuthenticationEvents.php +++ b/AuthenticationEvents.php @@ -24,16 +24,6 @@ final class AuthenticationEvents */ public const AUTHENTICATION_SUCCESS = 'security.authentication.success'; - /** - * The AUTHENTICATION_FAILURE event occurs after a user cannot be - * authenticated by any of the providers. - * - * @Event("Symfony\Component\Security\Core\Event\AuthenticationFailureEvent") - * - * @deprecated since Symfony 5.4, use {@see Event\LoginFailureEvent} instead - */ - public const AUTHENTICATION_FAILURE = 'security.authentication.failure'; - /** * Event aliases. * @@ -41,6 +31,5 @@ final class AuthenticationEvents */ public const ALIASES = [ AuthenticationSuccessEvent::class => self::AUTHENTICATION_SUCCESS, - AuthenticationFailureEvent::class => self::AUTHENTICATION_FAILURE, ]; } diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index 8da482d3..94d8a35b 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -88,7 +88,7 @@ private function decideAffirmative(TokenInterface $token, array $attributes, mix if (VoterInterface::ACCESS_DENIED === $result) { ++$deny; } elseif (VoterInterface::ACCESS_ABSTAIN !== $result) { - trigger_deprecation('symfony/security-core', '5.3', 'Returning "%s" in "%s::vote()" is deprecated, return one of "%s" constants: "ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN".', var_export($result, true), get_debug_type($voter), VoterInterface::class); + throw new \LogicException(sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.', get_debug_type($voter), VoterInterface::class, var_export($result, true))); } } @@ -125,7 +125,7 @@ private function decideConsensus(TokenInterface $token, array $attributes, mixed } elseif (VoterInterface::ACCESS_DENIED === $result) { ++$deny; } elseif (VoterInterface::ACCESS_ABSTAIN !== $result) { - trigger_deprecation('symfony/security-core', '5.3', 'Returning "%s" in "%s::vote()" is deprecated, return one of "%s" constants: "ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN".', var_export($result, true), get_debug_type($voter), VoterInterface::class); + throw new \LogicException(sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.', get_debug_type($voter), VoterInterface::class, var_export($result, true))); } } @@ -164,7 +164,7 @@ private function decideUnanimous(TokenInterface $token, array $attributes, mixed if (VoterInterface::ACCESS_GRANTED === $result) { ++$grant; } elseif (VoterInterface::ACCESS_ABSTAIN !== $result) { - trigger_deprecation('symfony/security-core', '5.3', 'Returning "%s" in "%s::vote()" is deprecated, return one of "%s" constants: "ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN".', var_export($result, true), get_debug_type($voter), VoterInterface::class); + throw new \LogicException(sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.', get_debug_type($voter), VoterInterface::class, var_export($result, true))); } } } @@ -198,7 +198,7 @@ private function decidePriority(TokenInterface $token, array $attributes, mixed } if (VoterInterface::ACCESS_ABSTAIN !== $result) { - trigger_deprecation('symfony/security-core', '5.3', 'Returning "%s" in "%s::vote()" is deprecated, return one of "%s" constants: "ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN".', var_export($result, true), get_debug_type($voter), VoterInterface::class); + throw new \LogicException(sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.', get_debug_type($voter), VoterInterface::class, var_export($result, true))); } } diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 8ee36f08..e00d1f18 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -28,35 +28,16 @@ class AuthorizationChecker implements AuthorizationCheckerInterface { private $tokenStorage; private $accessDecisionManager; - private $authenticationManager; - private $alwaysAuthenticate; private $exceptionOnNoToken; - public function __construct(TokenStorageInterface $tokenStorage, /*AccessDecisionManagerInterface*/ $accessDecisionManager, /*bool*/ $alwaysAuthenticate = false, /*bool*/ $exceptionOnNoToken = true) + public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, bool $exceptionOnNoToken = false) { - if ($accessDecisionManager instanceof AuthenticationManagerInterface) { - trigger_deprecation('symfony/security-core', '5.4', 'The $autenticationManager argument of "%s" is deprecated.', __METHOD__); - - $this->authenticationManager = $accessDecisionManager; - $accessDecisionManager = $alwaysAuthenticate; - $alwaysAuthenticate = $exceptionOnNoToken; - $exceptionOnNoToken = \func_num_args() > 4 ? func_get_arg(4) : true; - } - - if (false !== $alwaysAuthenticate) { - trigger_deprecation('symfony/security-core', '5.4', 'Not setting the 4th argument of "%s" to "false" is deprecated.', __METHOD__); - } if (false !== $exceptionOnNoToken) { - trigger_deprecation('symfony/security-core', '5.4', 'Not setting the 5th argument of "%s" to "false" is deprecated.', __METHOD__); - } - - if (!$accessDecisionManager instanceof AccessDecisionManagerInterface) { - throw new \TypeError(sprintf('Argument 2 of "%s" must be instance of "%s", "%s" given.', __METHOD__, AccessDecisionManagerInterface::class, get_debug_type($accessDecisionManager))); + throw new \LogicException('Argument $exceptionOnNoToken of "%s()" must be set to "false".', __METHOD__); } $this->tokenStorage = $tokenStorage; $this->accessDecisionManager = $accessDecisionManager; - $this->alwaysAuthenticate = $alwaysAuthenticate; $this->exceptionOnNoToken = $exceptionOnNoToken; } @@ -67,22 +48,7 @@ public function __construct(TokenStorageInterface $tokenStorage, /*AccessDecisio */ final public function isGranted(mixed $attribute, mixed $subject = null): bool { - if (null === ($token = $this->tokenStorage->getToken())) { - if ($this->exceptionOnNoToken) { - throw new AuthenticationCredentialsNotFoundException('The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.'); - } - - $token = new NullToken(); - } else { - $authenticated = true; - // @deprecated since Symfony 5.4 - if ($this->alwaysAuthenticate || !$authenticated = $token->isAuthenticated(false)) { - if (!($authenticated ?? true)) { - trigger_deprecation('symfony/core', '5.4', 'Returning false from "%s()" is deprecated and won\'t have any effect in Symfony 6.0 as security tokens will always be considered authenticated.'); - } - $this->tokenStorage->setToken($token = $this->authenticationManager->authenticate($token)); - } - } + $token = $this->tokenStorage->getToken() ?? new NullToken(); return $this->accessDecisionManager->decide($token, [$attribute], $subject); } diff --git a/Authorization/ExpressionLanguageProvider.php b/Authorization/ExpressionLanguageProvider.php index ba2ba264..74350039 100644 --- a/Authorization/ExpressionLanguageProvider.php +++ b/Authorization/ExpressionLanguageProvider.php @@ -13,7 +13,6 @@ use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; -use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; /** * Define some ExpressionLanguage functions. @@ -25,19 +24,10 @@ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface public function getFunctions() { return [ - new ExpressionFunction('is_anonymous', function () { - return 'trigger_deprecation("symfony/security-core", "5.4", "The \"is_anonymous()\" expression function is deprecated.") || ($token && $auth_checker->isGranted("IS_ANONYMOUS"))'; - }, function (array $variables) { - trigger_deprecation('symfony/security-core', '5.4', 'The "is_anonymous()" expression function is deprecated.'); - - return $variables['token'] && $variables['auth_checker']->isGranted('IS_ANONYMOUS'); - }), - - // @deprecated remove the ternary and always use IS_AUTHENTICATED in 6.0 new ExpressionFunction('is_authenticated', function () { - return 'defined("'.AuthenticatedVoter::class.'::IS_AUTHENTICATED") ? $auth_checker->isGranted("IS_AUTHENTICATED") : ($token && !$auth_checker->isGranted("IS_ANONYMOUS"))'; + return '$auth_checker->isGranted("IS_AUTHENTICATED")'; }, function (array $variables) { - return \defined(AuthenticatedVoter::class.'::IS_AUTHENTICATED') ? $variables['auth_checker']->isGranted('IS_AUTHENTICATED') : ($variables['token'] && !$variables['auth_checker']->isGranted('IS_ANONYMOUS')); + return $variables['auth_checker']->isGranted('IS_AUTHENTICATED'); }), new ExpressionFunction('is_fully_authenticated', function () { diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index d39ab0ad..3d338526 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -29,14 +29,6 @@ class AuthenticatedVoter implements VoterInterface { public const IS_AUTHENTICATED_FULLY = 'IS_AUTHENTICATED_FULLY'; public const IS_AUTHENTICATED_REMEMBERED = 'IS_AUTHENTICATED_REMEMBERED'; - /** - * @deprecated since Symfony 5.4 - */ - public const IS_AUTHENTICATED_ANONYMOUSLY = 'IS_AUTHENTICATED_ANONYMOUSLY'; - /** - * @deprecated since Symfony 5.4 - */ - public const IS_ANONYMOUS = 'IS_ANONYMOUS'; public const IS_AUTHENTICATED = 'IS_AUTHENTICATED'; public const IS_IMPERSONATOR = 'IS_IMPERSONATOR'; public const IS_REMEMBERED = 'IS_REMEMBERED'; @@ -62,9 +54,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes) foreach ($attributes as $attribute) { if (null === $attribute || (self::IS_AUTHENTICATED_FULLY !== $attribute && self::IS_AUTHENTICATED_REMEMBERED !== $attribute - && self::IS_AUTHENTICATED_ANONYMOUSLY !== $attribute && self::IS_AUTHENTICATED !== $attribute - && self::IS_ANONYMOUS !== $attribute && self::IS_IMPERSONATOR !== $attribute && self::IS_REMEMBERED !== $attribute)) { continue; @@ -83,20 +73,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes) return VoterInterface::ACCESS_GRANTED; } - if (self::IS_AUTHENTICATED_ANONYMOUSLY === $attribute - && ($this->authenticationTrustResolver->isAnonymous($token) - || $this->authenticationTrustResolver->isRememberMe($token) - || $this->authenticationTrustResolver->isFullFledged($token))) { - trigger_deprecation('symfony/security-core', '5.4', 'The "IS_AUTHENTICATED_ANONYMOUSLY" security attribute is deprecated, use "IS_AUTHENTICATED" or "IS_AUTHENTICATED_FULLY" instead if you want to check if the request is (fully) authenticated.'); - - return VoterInterface::ACCESS_GRANTED; - } - - // @deprecated $this->authenticationTrustResolver must implement isAuthenticated() in 6.0 - if (self::IS_AUTHENTICATED === $attribute - && (method_exists($this->authenticationTrustResolver, 'isAuthenticated') - ? $this->authenticationTrustResolver->isAuthenticated($token) - : (null !== $token && !$token instanceof NullToken))) { + if (self::IS_AUTHENTICATED === $attribute && $this->authenticationTrustResolver->isAuthenticated($token)) { return VoterInterface::ACCESS_GRANTED; } @@ -104,12 +81,6 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes) return VoterInterface::ACCESS_GRANTED; } - if (self::IS_ANONYMOUS === $attribute && $this->authenticationTrustResolver->isAnonymous($token)) { - trigger_deprecation('symfony/security-core', '5.4', 'The "IS_ANONYMOUSLY" security attribute is deprecated, anonymous no longer exists in version 6.'); - - return VoterInterface::ACCESS_GRANTED; - } - if (self::IS_IMPERSONATOR === $attribute && $token instanceof SwitchUserToken) { return VoterInterface::ACCESS_GRANTED; } diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index 1f187a2f..b8437a3b 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -40,10 +40,6 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes) continue; } - if ('ROLE_PREVIOUS_ADMIN' === $attribute) { - trigger_deprecation('symfony/security-core', '5.1', 'The ROLE_PREVIOUS_ADMIN role is deprecated and will be removed in version 6.0, use the IS_IMPERSONATOR attribute instead.'); - } - $result = VoterInterface::ACCESS_DENIED; foreach ($roles as $role) { if ($attribute === $role) { diff --git a/Event/AuthenticationFailureEvent.php b/Event/AuthenticationFailureEvent.php deleted file mode 100644 index 4e9562c2..00000000 --- a/Event/AuthenticationFailureEvent.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Event; - -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Http\Event\LoginFailureEvent; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" with the new authenticator system instead.', AuthenticationFailureEvent::class, LoginFailureEvent::class); - -/** - * This event is dispatched on authentication failure. - * - * @author Johannes M. Schmitt - * - * @deprecated since Symfony 5.3, use LoginFailureEvent with the new authenticator system instead - */ -final class AuthenticationFailureEvent extends AuthenticationEvent -{ - private $authenticationException; - - public function __construct(TokenInterface $token, AuthenticationException $ex) - { - parent::__construct($token); - - $this->authenticationException = $ex; - } - - public function getAuthenticationException(): AuthenticationException - { - return $this->authenticationException; - } -} diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 4f8b7ef7..a6df9636 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -37,18 +37,6 @@ public function getUserIdentifier(): ?string return $this->identifier; } - /** - * @return string - * - * @deprecated - */ - public function getUsername() - { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); - - return $this->identifier; - } - /** * Set the user identifier (e.g. username or e-mailaddress). */ @@ -57,16 +45,6 @@ public function setUserIdentifier(string $identifier): void $this->identifier = $identifier; } - /** - * @deprecated - */ - public function setUsername(string $username) - { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use setUserIdentifier() instead.', __METHOD__); - - $this->identifier = $username; - } - /** * {@inheritdoc} */ diff --git a/Exception/UsernameNotFoundException.php b/Exception/UsernameNotFoundException.php deleted file mode 100644 index e0d2d4a2..00000000 --- a/Exception/UsernameNotFoundException.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Exception; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', UsernameNotFoundException::class, UserNotFoundException::class); - -class_exists(UserNotFoundException::class); - -if (false) { - /** - * @deprecated since Symfony 5.3 to be removed in 6.0, use UserNotFoundException instead. - */ - class UsernameNotFoundException extends AuthenticationException - { - } -} diff --git a/Security.php b/Security.php index 4df0e339..ccd42813 100644 --- a/Security.php +++ b/Security.php @@ -41,18 +41,7 @@ public function getUser(): ?UserInterface return null; } - $user = $token->getUser(); - // @deprecated since 5.4, $user will always be a UserInterface instance - if (!\is_object($user)) { - return null; - } - - // @deprecated since 5.4, $user will always be a UserInterface instance - if (!$user instanceof UserInterface) { - return null; - } - - return $user; + return $token->getUser(); } /** diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index 84b16b01..ec252e26 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -79,7 +79,7 @@ public function verifySignatureHash(UserInterface $user, int $expires, string $h */ public function computeSignatureHash(UserInterface $user, int $expires): string { - $signatureFields = [base64_encode(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername()), $expires]; + $signatureFields = [base64_encode($user->getUserIdentifier()), $expires]; foreach ($this->signatureProperties as $property) { $value = $this->propertyAccessor->getValue($user, $property) ?? ''; diff --git a/Tests/Authentication/AuthenticationProviderManagerTest.php b/Tests/Authentication/AuthenticationProviderManagerTest.php deleted file mode 100644 index 661ffa45..00000000 --- a/Tests/Authentication/AuthenticationProviderManagerTest.php +++ /dev/null @@ -1,210 +0,0 @@ - - * - * 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\Authentication; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; -use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\AuthenticationEvents; -use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent; -use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; -use Symfony\Component\Security\Core\Exception\AccountStatusException; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; -use Symfony\Component\Security\Core\User\InMemoryUser; - -/** - * @group legacy - */ -class AuthenticationProviderManagerTest extends TestCase -{ - public function testAuthenticateWithoutProviders() - { - $this->expectException(\InvalidArgumentException::class); - new AuthenticationProviderManager([]); - } - - public function testAuthenticateWithProvidersWithIncorrectInterface() - { - $this->expectException(\InvalidArgumentException::class); - (new AuthenticationProviderManager([ - new \stdClass(), - ]))->authenticate($this->createMock(TokenInterface::class)); - } - - public function testAuthenticateWhenNoProviderSupportsToken() - { - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(false), - ]); - - try { - $manager->authenticate($token = $this->createMock(TokenInterface::class)); - $this->fail(); - } catch (ProviderNotFoundException $e) { - $this->assertSame($token, $e->getToken()); - } - } - - public function testAuthenticateWhenProviderReturnsAccountStatusException() - { - $secondAuthenticationProvider = $this->createMock(AuthenticationProviderInterface::class); - - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, null, AccountStatusException::class), - $secondAuthenticationProvider, - ]); - - // AccountStatusException stops authentication - $secondAuthenticationProvider->expects($this->never())->method('supports'); - - try { - $manager->authenticate($token = $this->createMock(TokenInterface::class)); - $this->fail(); - } catch (AccountStatusException $e) { - $this->assertSame($token, $e->getToken()); - } - } - - public function testAuthenticateWhenProviderReturnsAuthenticationException() - { - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, null, AuthenticationException::class), - ]); - - try { - $manager->authenticate($token = $this->createMock(TokenInterface::class)); - $this->fail(); - } catch (AuthenticationException $e) { - $this->assertSame($token, $e->getToken()); - } - } - - public function testAuthenticateWhenOneReturnsAuthenticationExceptionButNotAll() - { - $expected = $this->createMock(TokenInterface::class); - $expected->expects($this->any())->method('getUser')->willReturn(new InMemoryUser('wouter', null)); - - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, null, AuthenticationException::class), - $this->getAuthenticationProvider(true, $expected), - ]); - - $token = $manager->authenticate($this->createMock(TokenInterface::class)); - $this->assertSame($expected, $token); - } - - public function testAuthenticateReturnsTokenOfTheFirstMatchingProvider() - { - $second = $this->createMock(AuthenticationProviderInterface::class); - $second - ->expects($this->never()) - ->method('supports') - ; - $expected = $this->createMock(TokenInterface::class); - $expected->expects($this->any())->method('getUser')->willReturn(new InMemoryUser('wouter', null)); - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, $expected), - $second, - ]); - - $token = $manager->authenticate($this->createMock(TokenInterface::class)); - $this->assertSame($expected, $token); - } - - public function testEraseCredentialFlag() - { - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')), - ]); - - $token = $manager->authenticate($this->createMock(TokenInterface::class)); - $this->assertEquals('', $token->getCredentials()); - - $manager = new AuthenticationProviderManager([ - $this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')), - ], false); - - $token = $manager->authenticate($this->createMock(TokenInterface::class)); - $this->assertEquals('bar', $token->getCredentials()); - } - - public function testAuthenticateDispatchesAuthenticationFailureEvent() - { - $token = new UsernamePasswordToken('foo', 'bar', 'key'); - $provider = $this->createMock(AuthenticationProviderInterface::class); - $provider->expects($this->once())->method('supports')->willReturn(true); - $provider->expects($this->once())->method('authenticate')->willThrowException($exception = new AuthenticationException()); - - $dispatcher = $this->createMock(EventDispatcherInterface::class); - $dispatcher - ->expects($this->once()) - ->method('dispatch') - ->with($this->equalTo(new AuthenticationFailureEvent($token, $exception)), AuthenticationEvents::AUTHENTICATION_FAILURE); - - $manager = new AuthenticationProviderManager([$provider]); - $manager->setEventDispatcher($dispatcher); - - try { - $manager->authenticate($token); - $this->fail('->authenticate() should rethrow exceptions'); - } catch (AuthenticationException $e) { - $this->assertSame($token, $exception->getToken()); - } - } - - public function testAuthenticateDispatchesAuthenticationSuccessEvent() - { - $token = new UsernamePasswordToken('foo', 'bar', 'key'); - - $provider = $this->createMock(AuthenticationProviderInterface::class); - $provider->expects($this->once())->method('supports')->willReturn(true); - $provider->expects($this->once())->method('authenticate')->willReturn($token); - - $dispatcher = $this->createMock(EventDispatcherInterface::class); - $dispatcher - ->expects($this->once()) - ->method('dispatch') - ->with($this->equalTo(new AuthenticationSuccessEvent($token)), AuthenticationEvents::AUTHENTICATION_SUCCESS); - - $manager = new AuthenticationProviderManager([$provider]); - $manager->setEventDispatcher($dispatcher); - - $this->assertSame($token, $manager->authenticate($token)); - } - - protected function getAuthenticationProvider($supports, $token = null, $exception = null) - { - $provider = $this->createMock(AuthenticationProviderInterface::class); - $provider->expects($this->once()) - ->method('supports') - ->willReturn($supports) - ; - - if (null !== $token) { - $provider->expects($this->once()) - ->method('authenticate') - ->willReturn($token) - ; - } elseif (null !== $exception) { - $provider->expects($this->once()) - ->method('authenticate') - ->willThrowException($this->getMockBuilder($exception)->setMethods(null)->getMock()) - ; - } - - return $provider; - } -} diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index aeddb48d..aae11bcf 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\InMemoryUser; @@ -21,18 +20,6 @@ class AuthenticationTrustResolverTest extends TestCase { - /** - * @group legacy - */ - public function testIsAnonymous() - { - $resolver = new AuthenticationTrustResolver(); - $this->assertFalse($resolver->isAnonymous(null)); - $this->assertFalse($resolver->isAnonymous($this->getToken())); - $this->assertFalse($resolver->isAnonymous($this->getRememberMeToken())); - $this->assertFalse($resolver->isAnonymous(new FakeCustomToken())); - } - public function testIsRememberMe() { $resolver = new AuthenticationTrustResolver(); @@ -63,87 +50,17 @@ public function testIsAuthenticated() $this->assertTrue($resolver->isAuthenticated(new FakeCustomToken())); } - /** - * @group legacy - */ - public function testIsAnonymousWithClassAsConstructorButStillExtending() - { - $resolver = $this->getResolver(); - - $this->assertFalse($resolver->isAnonymous(null)); - $this->assertFalse($resolver->isAnonymous($this->getToken())); - $this->assertFalse($resolver->isAnonymous($this->getRememberMeToken())); - } - - public function testIsRememberMeWithClassAsConstructorButStillExtending() - { - $resolver = $this->getResolver(); - - $this->assertFalse($resolver->isRememberMe(null)); - $this->assertFalse($resolver->isRememberMe($this->getToken())); - $this->assertTrue($resolver->isRememberMe($this->getRememberMeToken())); - $this->assertTrue($resolver->isRememberMe(new RealCustomRememberMeToken())); - } - - public function testisFullFledgedWithClassAsConstructorButStillExtending() - { - $resolver = $this->getResolver(); - - $this->assertFalse($resolver->isFullFledged(null)); - $this->assertFalse($resolver->isFullFledged($this->getRememberMeToken())); - $this->assertFalse($resolver->isFullFledged(new RealCustomRememberMeToken())); - $this->assertTrue($resolver->isFullFledged($this->getToken())); - } - - /** - * @group legacy - */ - public function testLegacy() - { - $resolver = $this->getResolver(); - - $this->assertTrue($resolver->isAnonymous($this->getAnonymousToken())); - $this->assertTrue($resolver->isAnonymous($this->getRealCustomAnonymousToken())); - - $this->assertFalse($resolver->isRememberMe($this->getAnonymousToken())); - - $this->assertFalse($resolver->isFullFledged($this->getAnonymousToken())); - $this->assertFalse($resolver->isFullFledged($this->getRealCustomAnonymousToken())); - } - protected function getToken() { return $this->createMock(TokenInterface::class); } - protected function getAnonymousToken() - { - return new AnonymousToken('secret', 'anon.'); - } - - private function getRealCustomAnonymousToken() - { - return new class() extends AnonymousToken { - public function __construct() - { - } - }; - } - protected function getRememberMeToken() { $user = new InMemoryUser('wouter', '', ['ROLE_USER']); return new RememberMeToken($user, 'main', 'secret'); } - - protected function getResolver() - { - return new AuthenticationTrustResolver( - AnonymousToken::class, - RememberMeToken::class - ); - } } class FakeCustomToken implements TokenInterface @@ -192,15 +109,6 @@ public function getUserIdentifier(): string { } - public function isAuthenticated(): bool - { - return true; - } - - public function setAuthenticated(bool $isAuthenticated) - { - } - public function eraseCredentials() { } diff --git a/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php b/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php deleted file mode 100644 index 08127b6c..00000000 --- a/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * 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\Authentication\Provider; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; - -/** - * @group legacy - */ -class AnonymousAuthenticationProviderTest extends TestCase -{ - public function testSupports() - { - $provider = $this->getProvider('foo'); - - $this->assertTrue($provider->supports($this->getSupportedToken('foo'))); - $this->assertFalse($provider->supports($this->createMock(TokenInterface::class))); - } - - public function testAuthenticateWhenTokenIsNotSupported() - { - $this->expectException(AuthenticationException::class); - $this->expectExceptionMessage('The token is not supported by this authentication provider.'); - $provider = $this->getProvider('foo'); - - $provider->authenticate($this->createMock(TokenInterface::class)); - } - - public function testAuthenticateWhenSecretIsNotValid() - { - $this->expectException(BadCredentialsException::class); - $provider = $this->getProvider('foo'); - - $provider->authenticate($this->getSupportedToken('bar')); - } - - public function testAuthenticate() - { - $provider = $this->getProvider('foo'); - $token = $this->getSupportedToken('foo'); - - $this->assertSame($token, $provider->authenticate($token)); - } - - protected function getSupportedToken($secret) - { - $token = $this->getMockBuilder(AnonymousToken::class)->setMethods(['getSecret'])->disableOriginalConstructor()->getMock(); - $token->expects($this->any()) - ->method('getSecret') - ->willReturn($secret) - ; - - return $token; - } - - protected function getProvider($secret) - { - return new AnonymousAuthenticationProvider($secret); - } -} diff --git a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php b/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php deleted file mode 100644 index 8736b57c..00000000 --- a/Tests/Authentication/Provider/DaoAuthenticationProviderTest.php +++ /dev/null @@ -1,356 +0,0 @@ - - * - * 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\Authentication\Provider; - -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; -use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher; -use Symfony\Component\PasswordHasher\PasswordHasherInterface; -use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\UserNotFoundException; -use Symfony\Component\Security\Core\User\InMemoryUser; -use Symfony\Component\Security\Core\User\InMemoryUserProvider; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; - -/** - * @group legacy - */ -class DaoAuthenticationProviderTest extends TestCase -{ - use ExpectDeprecationTrait; - - public function testRetrieveUserWhenUsernameIsNotFound() - { - $this->expectException(UserNotFoundException::class); - $userProvider = new InMemoryUserProvider(); - - $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); - $method = new \ReflectionMethod($provider, 'retrieveUser'); - $method->setAccessible(true); - - $method->invoke($provider, 'fabien', $this->getSupportedToken()); - } - - public function testRetrieveUserWhenAnExceptionOccurs() - { - $this->expectException(AuthenticationServiceException::class); - $userProvider = $this->createMock(InMemoryUserProvider::class); - $userProvider->expects($this->once()) - ->method('loadUserByIdentifier') - ->willThrowException(new \RuntimeException()) - ; - - $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); - $method = new \ReflectionMethod($provider, 'retrieveUser'); - $method->setAccessible(true); - - $method->invoke($provider, 'fabien', $this->getSupportedToken()); - } - - public function testRetrieveUserReturnsUserFromTokenOnReauthentication() - { - $userProvider = $this->createMock(InMemoryUserProvider::class); - $userProvider->expects($this->never()) - ->method('loadUserByIdentifier') - ; - - $user = new TestUser(); - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getUser') - ->willReturn($user) - ; - - $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); - $reflection = new \ReflectionMethod($provider, 'retrieveUser'); - $reflection->setAccessible(true); - $result = $reflection->invoke($provider, 'someUser', $token); - - $this->assertSame($user, $result); - } - - public function testRetrieveUser() - { - $userProvider = new InMemoryUserProvider(['fabien' => []]); - - $provider = new DaoAuthenticationProvider($userProvider, $this->createMock(UserCheckerInterface::class), 'key', $this->createMock(PasswordHasherFactoryInterface::class)); - $method = new \ReflectionMethod($provider, 'retrieveUser'); - $method->setAccessible(true); - - $this->assertEquals('fabien', $method->invoke($provider, 'fabien', $this->getSupportedToken())->getUserIdentifier()); - } - - public function testCheckAuthenticationWhenCredentialsAreEmpty() - { - $this->expectException(BadCredentialsException::class); - $hasher = $this->getMockBuilder(PasswordHasherInterface::class)->getMock(); - $hasher - ->expects($this->never()) - ->method('verify') - ; - - $provider = $this->getProvider(null, null, $hasher); - $method = new \ReflectionMethod($provider, 'checkAuthentication'); - $method->setAccessible(true); - - $token = $this->getSupportedToken(); - $token - ->expects($this->once()) - ->method('getCredentials') - ->willReturn('') - ; - - $method->invoke($provider, new TestUser(), $token); - } - - public function testCheckAuthenticationWhenCredentialsAre0() - { - $hasher = $this->createMock(PasswordHasherInterface::class); - $hasher - ->expects($this->once()) - ->method('verify') - ->willReturn(true) - ; - - $provider = $this->getProvider(null, null, $hasher); - $method = new \ReflectionMethod($provider, 'checkAuthentication'); - $method->setAccessible(true); - - $token = $this->getSupportedToken(); - $token - ->expects($this->once()) - ->method('getCredentials') - ->willReturn('0') - ; - - $method->invoke( - $provider, - new InMemoryUser('username', 'password'), - $token - ); - } - - public function testCheckAuthenticationWhenCredentialsAreNotValid() - { - $this->expectException(BadCredentialsException::class); - $hasher = $this->createMock(PasswordHasherInterface::class); - $hasher->expects($this->once()) - ->method('verify') - ->willReturn(false) - ; - - $provider = $this->getProvider(null, null, $hasher); - $method = new \ReflectionMethod($provider, 'checkAuthentication'); - $method->setAccessible(true); - - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getCredentials') - ->willReturn('foo') - ; - - $method->invoke($provider, new InMemoryUser('username', 'password'), $token); - } - - public function testCheckAuthenticationDoesNotReauthenticateWhenPasswordHasChanged() - { - $this->expectException(BadCredentialsException::class); - $user = $this->createMock(TestUser::class); - $user->expects($this->once()) - ->method('getPassword') - ->willReturn('foo') - ; - - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getUser') - ->willReturn($user); - - $dbUser = $this->createMock(TestUser::class); - $dbUser->expects($this->once()) - ->method('getPassword') - ->willReturn('newFoo') - ; - - $provider = $this->getProvider(); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - $reflection->invoke($provider, $dbUser, $token); - } - - public function testCheckAuthenticationWhenTokenNeedsReauthenticationWorksWithoutOriginalCredentials() - { - $user = $this->createMock(TestUser::class); - $user->expects($this->once()) - ->method('getPassword') - ->willReturn('foo') - ; - - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getUser') - ->willReturn($user); - - $dbUser = $this->createMock(TestUser::class); - $dbUser->expects($this->once()) - ->method('getPassword') - ->willReturn('foo') - ; - - $provider = $this->getProvider(); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - $reflection->invoke($provider, $dbUser, $token); - } - - public function testCheckAuthentication() - { - $hasher = $this->createMock(PasswordHasherInterface::class); - $hasher->expects($this->once()) - ->method('verify') - ->willReturn(true) - ; - - $provider = $this->getProvider(null, null, $hasher); - $method = new \ReflectionMethod($provider, 'checkAuthentication'); - $method->setAccessible(true); - - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getCredentials') - ->willReturn('foo') - ; - - $method->invoke($provider, new InMemoryUser('username', 'password'), $token); - } - - public function testPasswordUpgrades() - { - $user = new InMemoryUser('user', 'pwd'); - - $hasher = $this->createMock(PasswordHasherInterface::class); - $hasher->expects($this->once()) - ->method('verify') - ->willReturn(true) - ; - $hasher->expects($this->once()) - ->method('hash') - ->willReturn('foobar') - ; - $hasher->expects($this->once()) - ->method('needsRehash') - ->willReturn(true) - ; - - $provider = $this->getProvider(null, null, $hasher); - - $userProvider = ((array) $provider)[sprintf("\0%s\0userProvider", DaoAuthenticationProvider::class)]; - $userProvider->expects($this->once()) - ->method('upgradePassword') - ->with($user, 'foobar') - ; - - $method = new \ReflectionMethod($provider, 'checkAuthentication'); - $method->setAccessible(true); - - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getCredentials') - ->willReturn('foo') - ; - - $method->invoke($provider, $user, $token); - } - - protected function getSupportedToken() - { - $mock = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getCredentials', 'getUser', 'getProviderKey'])->disableOriginalConstructor()->getMock(); - $mock - ->expects($this->any()) - ->method('getProviderKey') - ->willReturn('key') - ; - - return $mock; - } - - protected function getProvider($user = null, $userChecker = null, $passwordHasher = null, $userProvider = null) - { - if (null === $userProvider) { - $userProvider = $this->createMock(PasswordUpgraderProvider::class); - if (null !== $user) { - $userProvider->expects($this->once()) - ->method('loadUserByIdentifier') - ->willReturn($user) - ; - } - } - - if (null === $userChecker) { - $userChecker = $this->createMock(UserCheckerInterface::class); - } - - if (null === $passwordHasher) { - $passwordHasher = new PlaintextPasswordHasher(); - } - - $hasherFactory = $this->createMock(PasswordHasherFactoryInterface::class); - $hasherFactory - ->expects($this->any()) - ->method('getPasswordHasher') - ->willReturn($passwordHasher) - ; - - return new DaoAuthenticationProvider($userProvider, $userChecker, 'key', $hasherFactory); - } -} - -class TestUser implements PasswordAuthenticatedUserInterface, UserInterface -{ - public function getRoles(): array - { - return []; - } - - public function getPassword(): ?string - { - return 'secret'; - } - - public function getUsername(): string - { - return 'jane_doe'; - } - - public function getUserIdentifier(): string - { - return 'jane_doe'; - } - - public function eraseCredentials() - { - } -} -interface PasswordUpgraderProvider extends UserProviderInterface, PasswordUpgraderInterface -{ - public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void; - - public function loadUserByIdentifier(string $identifier): UserInterface; -} diff --git a/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php deleted file mode 100644 index 5bad9611..00000000 --- a/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php +++ /dev/null @@ -1,233 +0,0 @@ - - * - * 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\Authentication\Provider; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Ldap\Adapter\CollectionInterface; -use Symfony\Component\Ldap\Adapter\QueryInterface; -use Symfony\Component\Ldap\Entry; -use Symfony\Component\Ldap\Exception\ConnectionException; -use Symfony\Component\Ldap\LdapInterface; -use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\User\InMemoryUser; -use Symfony\Component\Security\Core\User\InMemoryUserProvider; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; - -/** - * @requires extension ldap - * @group legacy - */ -class LdapBindAuthenticationProviderTest extends TestCase -{ - public function testEmptyPasswordShouldThrowAnException() - { - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('The presented password must not be empty.'); - $userProvider = $this->createMock(UserProviderInterface::class); - $ldap = $this->createMock(LdapInterface::class); - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, new InMemoryUser('foo', null), new UsernamePasswordToken('foo', '', 'key')); - } - - public function testNullPasswordShouldThrowAnException() - { - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('The presented password must not be empty.'); - $userProvider = $this->createMock(UserProviderInterface::class); - $ldap = $this->createMock(LdapInterface::class); - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, new InMemoryUser('foo', null), new UsernamePasswordToken('foo', null, 'key')); - } - - public function testBindFailureShouldThrowAnException() - { - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('The presented password is invalid.'); - $userProvider = $this->createMock(UserProviderInterface::class); - $ldap = $this->createMock(LdapInterface::class); - $ldap - ->expects($this->once()) - ->method('bind') - ->willThrowException(new ConnectionException()) - ; - $ldap->method('escape')->willReturnArgument(0); - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, new InMemoryUser('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); - } - - public function testRetrieveUser() - { - $userProvider = $this->createMock(InMemoryUserProvider::class); - $userProvider - ->expects($this->once()) - ->method('loadUserByIdentifier') - ->with('foo') - ; - $ldap = $this->createMock(LdapInterface::class); - - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); - $reflection = new \ReflectionMethod($provider, 'retrieveUser'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, 'foo', new UsernamePasswordToken('foo', 'bar', 'key')); - } - - public function testQueryForDn() - { - $userProvider = $this->createMock(UserProviderInterface::class); - - $collection = new class([new Entry('')]) extends \ArrayObject implements CollectionInterface { - public function toArray(): array - { - return $this->getArrayCopy(); - } - }; - - $query = $this->createMock(QueryInterface::class); - $query - ->expects($this->once()) - ->method('execute') - ->willReturn($collection) - ; - - $ldap = $this->createMock(LdapInterface::class); - $ldap - ->method('bind') - ->withConsecutive( - ['elsa', 'test1234A$'] - ); - $ldap - ->expects($this->once()) - ->method('escape') - ->with('foo', '') - ->willReturn('foo') - ; - $ldap - ->expects($this->once()) - ->method('query') - ->with('{username}', 'foobar') - ->willReturn($query) - ; - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap, '{username}', true, 'elsa', 'test1234A$'); - $provider->setQueryString('{username}bar'); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, new InMemoryUser('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); - } - - public function testQueryWithUserForDn() - { - $userProvider = $this->createMock(UserProviderInterface::class); - - $collection = new class([new Entry('')]) extends \ArrayObject implements CollectionInterface { - public function toArray(): array - { - return $this->getArrayCopy(); - } - }; - - $query = $this->createMock(QueryInterface::class); - $query - ->expects($this->once()) - ->method('execute') - ->willReturn($collection) - ; - - $ldap = $this->createMock(LdapInterface::class); - $ldap - ->method('bind') - ->withConsecutive( - ['elsa', 'test1234A$'] - ); - $ldap - ->expects($this->once()) - ->method('escape') - ->with('foo', '') - ->willReturn('foo') - ; - $ldap - ->expects($this->once()) - ->method('query') - ->with('{username}', 'foobar') - ->willReturn($query) - ; - - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap, '{username}', true, 'elsa', 'test1234A$'); - $provider->setQueryString('{username}bar'); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, new InMemoryUser('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); - } - - public function testEmptyQueryResultShouldThrowAnException() - { - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('The presented username is invalid.'); - $userProvider = $this->createMock(UserProviderInterface::class); - - $collection = $this->createMock(CollectionInterface::class); - - $query = $this->createMock(QueryInterface::class); - $query - ->expects($this->once()) - ->method('execute') - ->willReturn($collection) - ; - - $ldap = $this->createMock(LdapInterface::class); - $ldap - ->method('bind') - ->withConsecutive( - ['elsa', 'test1234A$'] - ); - $ldap - ->expects($this->once()) - ->method('query') - ->willReturn($query) - ; - $ldap->method('escape')->willReturnArgument(0); - $userChecker = $this->createMock(UserCheckerInterface::class); - - $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap, '{username}', true, 'elsa', 'test1234A$'); - $provider->setQueryString('{username}bar'); - $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); - $reflection->setAccessible(true); - - $reflection->invoke($provider, new InMemoryUser('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); - } -} diff --git a/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php b/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php deleted file mode 100644 index e178d9c7..00000000 --- a/Tests/Authentication/Provider/PreAuthenticatedAuthenticationProviderTest.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * 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\Authentication\Provider; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider; -use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\LockedException; -use Symfony\Component\Security\Core\User\InMemoryUserProvider; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class PreAuthenticatedAuthenticationProviderTest extends TestCase -{ - public function testSupports() - { - $provider = $this->getProvider(); - - $this->assertTrue($provider->supports($this->getSupportedToken())); - $this->assertFalse($provider->supports($this->createMock(TokenInterface::class))); - - $token = $this->createMock(PreAuthenticatedToken::class); - $token - ->expects($this->once()) - ->method('getFirewallName') - ->willReturn('foo') - ; - $this->assertFalse($provider->supports($token)); - } - - public function testAuthenticateWhenTokenIsNotSupported() - { - $this->expectException(AuthenticationException::class); - $this->expectExceptionMessage('The token is not supported by this authentication provider.'); - $provider = $this->getProvider(); - - $provider->authenticate($this->createMock(TokenInterface::class)); - } - - public function testAuthenticateWhenNoUserIsSet() - { - $this->expectException(BadCredentialsException::class); - $provider = $this->getProvider(); - $provider->authenticate($this->getSupportedToken('')); - } - - public function testAuthenticate() - { - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->once()) - ->method('getRoles') - ->willReturn([]) - ; - $provider = $this->getProvider($user); - - $token = $provider->authenticate($this->getSupportedToken('fabien', 'pass')); - $this->assertInstanceOf(PreAuthenticatedToken::class, $token); - $this->assertEquals('pass', $token->getCredentials()); - $this->assertEquals('key', $token->getFirewallName()); - $this->assertEquals([], $token->getRoleNames()); - $this->assertEquals(['foo' => 'bar'], $token->getAttributes(), '->authenticate() copies token attributes'); - $this->assertSame($user, $token->getUser()); - } - - public function testAuthenticateWhenUserCheckerThrowsException() - { - $this->expectException(LockedException::class); - $user = $this->createMock(UserInterface::class); - - $userChecker = $this->createMock(UserCheckerInterface::class); - $userChecker->expects($this->once()) - ->method('checkPostAuth') - ->willThrowException(new LockedException()) - ; - - $provider = $this->getProvider($user, $userChecker); - - $provider->authenticate($this->getSupportedToken('fabien')); - } - - protected function getSupportedToken($user = false, $credentials = false) - { - $token = $this->getMockBuilder(PreAuthenticatedToken::class)->setMethods(['getUser', 'getCredentials', 'getFirewallName'])->disableOriginalConstructor()->getMock(); - if (false !== $user) { - $token->expects($this->once()) - ->method('getUser') - ->willReturn($user) - ; - } - if (false !== $credentials) { - $token->expects($this->once()) - ->method('getCredentials') - ->willReturn($credentials) - ; - } - - $token - ->expects($this->any()) - ->method('getFirewallName') - ->willReturn('key') - ; - - $token->setAttributes(['foo' => 'bar']); - - return $token; - } - - protected function getProvider($user = null, $userChecker = null) - { - $userProvider = $this->createMock(InMemoryUserProvider::class); - if (null !== $user) { - $userProvider->expects($this->once()) - ->method('loadUserByIdentifier') - ->willReturn($user) - ; - } - - if (null === $userChecker) { - $userChecker = $this->createMock(UserCheckerInterface::class); - } - - return new PreAuthenticatedAuthenticationProvider($userProvider, $userChecker, 'key'); - } -} diff --git a/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php b/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php deleted file mode 100644 index 9a6a417b..00000000 --- a/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * 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\Authentication\Provider; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider; -use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\DisabledException; -use Symfony\Component\Security\Core\Exception\LogicException; -use Symfony\Component\Security\Core\User\InMemoryUser; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class RememberMeAuthenticationProviderTest extends TestCase -{ - public function testSupports() - { - $provider = $this->getProvider(); - - $this->assertTrue($provider->supports($this->getSupportedToken())); - $this->assertFalse($provider->supports($this->createMock(TokenInterface::class))); - $this->assertFalse($provider->supports($this->createMock(RememberMeToken::class))); - } - - public function testAuthenticateWhenTokenIsNotSupported() - { - $this->expectException(AuthenticationException::class); - $this->expectExceptionMessage('The token is not supported by this authentication provider.'); - $provider = $this->getProvider(); - - $token = $this->createMock(TokenInterface::class); - $provider->authenticate($token); - } - - public function testAuthenticateWhenSecretsDoNotMatch() - { - $this->expectException(BadCredentialsException::class); - $provider = $this->getProvider(null, 'secret1'); - $token = $this->getSupportedToken(null, 'secret2'); - - $provider->authenticate($token); - } - - public function testAuthenticateThrowsOnNonUserInterfaceInstance() - { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Method "Symfony\Component\Security\Core\Authentication\Token\RememberMeToken::getUser()" must return a "Symfony\Component\Security\Core\User\UserInterface" instance, "string" returned.'); - - $provider = $this->getProvider(); - $token = new RememberMeToken(new InMemoryUser('dummyuser', null), 'foo', 'test'); - $token->setUser('stringish-user'); - $provider->authenticate($token); - } - - public function testAuthenticateWhenPreChecksFails() - { - $this->expectException(DisabledException::class); - $userChecker = $this->createMock(UserCheckerInterface::class); - $userChecker->expects($this->once()) - ->method('checkPreAuth') - ->willThrowException(new DisabledException()); - - $provider = $this->getProvider($userChecker); - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticate() - { - $user = $this->createMock(UserInterface::class); - $user->expects($this->exactly(2)) - ->method('getRoles') - ->willReturn(['ROLE_FOO']); - - $provider = $this->getProvider(); - - $token = $this->getSupportedToken($user); - $authToken = $provider->authenticate($token); - - $this->assertInstanceOf(RememberMeToken::class, $authToken); - $this->assertSame($user, $authToken->getUser()); - $this->assertEquals(['ROLE_FOO'], $authToken->getRoleNames()); - $this->assertEquals('', $authToken->getCredentials()); - } - - protected function getSupportedToken($user = null, $secret = 'test') - { - if (null === $user) { - $user = $this->createMock(UserInterface::class); - $user - ->expects($this->any()) - ->method('getRoles') - ->willReturn([]); - } - - $token = $this->getMockBuilder(RememberMeToken::class)->setMethods(['getFirewallName'])->setConstructorArgs([$user, 'foo', $secret])->getMock(); - $token - ->expects($this->once()) - ->method('getFirewallName') - ->willReturn('foo'); - - return $token; - } - - protected function getProvider($userChecker = null, $key = 'test') - { - if (null === $userChecker) { - $userChecker = $this->createMock(UserCheckerInterface::class); - } - - return new RememberMeAuthenticationProvider($userChecker, $key, 'foo'); - } -} diff --git a/Tests/Authentication/Provider/UserAuthenticationProviderTest.php b/Tests/Authentication/Provider/UserAuthenticationProviderTest.php deleted file mode 100644 index c4bcd8f5..00000000 --- a/Tests/Authentication/Provider/UserAuthenticationProviderTest.php +++ /dev/null @@ -1,254 +0,0 @@ - - * - * 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\Authentication\Provider; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider; -use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Exception\AccountExpiredException; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; -use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; -use Symfony\Component\Security\Core\Exception\UserNotFoundException; -use Symfony\Component\Security\Core\User\InMemoryUser; -use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class UserAuthenticationProviderTest extends TestCase -{ - public function testSupports() - { - $provider = $this->getProvider(); - - $this->assertTrue($provider->supports($this->getSupportedToken())); - $this->assertFalse($provider->supports($this->createMock(TokenInterface::class))); - } - - public function testAuthenticateWhenTokenIsNotSupported() - { - $this->expectException(AuthenticationException::class); - $this->expectExceptionMessage('The token is not supported by this authentication provider.'); - $provider = $this->getProvider(); - - $provider->authenticate($this->createMock(TokenInterface::class)); - } - - public function testAuthenticateWhenUsernameIsNotFound() - { - $this->expectException(UserNotFoundException::class); - $provider = $this->getProvider(false, false); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willThrowException(new UserNotFoundException()) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenUsernameIsNotFoundAndHideIsTrue() - { - $this->expectException(BadCredentialsException::class); - $provider = $this->getProvider(false, true); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willThrowException(new UserNotFoundException()) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenCredentialsAreInvalidAndHideIsTrue() - { - $provider = $this->getProvider(); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($this->createMock(UserInterface::class)) - ; - $provider->expects($this->once()) - ->method('checkAuthentication') - ->willThrowException(new BadCredentialsException()) - ; - - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('Bad credentials.'); - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenProviderDoesNotReturnAnUserInterface() - { - $this->expectException(AuthenticationServiceException::class); - $provider = $this->getProvider(false, true); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn(null) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenPreChecksFails() - { - $this->expectException(BadCredentialsException::class); - $userChecker = $this->createMock(UserCheckerInterface::class); - $userChecker->expects($this->once()) - ->method('checkPreAuth') - ->willThrowException(new CredentialsExpiredException()) - ; - - $provider = $this->getProvider($userChecker); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($this->createMock(UserInterface::class)) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenPostChecksFails() - { - $this->expectException(BadCredentialsException::class); - $userChecker = $this->createMock(UserCheckerInterface::class); - $userChecker->expects($this->once()) - ->method('checkPostAuth') - ->willThrowException(new AccountExpiredException()) - ; - - $provider = $this->getProvider($userChecker); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($this->createMock(UserInterface::class)) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenPostCheckAuthenticationFails() - { - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('Bad credentials'); - $provider = $this->getProvider(); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($this->createMock(UserInterface::class)) - ; - $provider->expects($this->once()) - ->method('checkAuthentication') - ->willThrowException(new CredentialsExpiredException()) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticateWhenPostCheckAuthenticationFailsWithHideFalse() - { - $this->expectException(BadCredentialsException::class); - $this->expectExceptionMessage('Foo'); - $provider = $this->getProvider(false, false); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($this->createMock(UserInterface::class)) - ; - $provider->expects($this->once()) - ->method('checkAuthentication') - ->willThrowException(new BadCredentialsException('Foo')) - ; - - $provider->authenticate($this->getSupportedToken()); - } - - public function testAuthenticate() - { - $user = $this->createMock(UserInterface::class); - $user->expects($this->once()) - ->method('getRoles') - ->willReturn(['ROLE_FOO']) - ; - - $provider = $this->getProvider(); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($user) - ; - - $token = $this->getSupportedToken(); - $token->expects($this->once()) - ->method('getCredentials') - ->willReturn('foo') - ; - - $authToken = $provider->authenticate($token); - - $this->assertInstanceOf(UsernamePasswordToken::class, $authToken); - $this->assertSame($user, $authToken->getUser()); - $this->assertEquals(['ROLE_FOO'], $authToken->getRoleNames()); - $this->assertEquals('foo', $authToken->getCredentials()); - $this->assertEquals(['foo' => 'bar'], $authToken->getAttributes(), '->authenticate() copies token attributes'); - } - - public function testAuthenticatePreservesOriginalToken() - { - $user = $this->createMock(UserInterface::class); - $user->expects($this->once()) - ->method('getRoles') - ->willReturn(['ROLE_FOO']) - ; - - $provider = $this->getProvider(); - $provider->expects($this->once()) - ->method('retrieveUser') - ->willReturn($user) - ; - - $originalToken = $this->createMock(TokenInterface::class); - $token = new SwitchUserToken(new InMemoryUser('wouter', null), 'foo', 'key', [], $originalToken); - $token->setAttributes(['foo' => 'bar']); - - $authToken = $provider->authenticate($token); - - $this->assertInstanceOf(SwitchUserToken::class, $authToken); - $this->assertSame($originalToken, $authToken->getOriginalToken()); - $this->assertSame($user, $authToken->getUser()); - $this->assertContains('ROLE_FOO', $authToken->getRoleNames()); - $this->assertEquals('foo', $authToken->getCredentials()); - $this->assertEquals(['foo' => 'bar'], $authToken->getAttributes(), '->authenticate() copies token attributes'); - } - - protected function getSupportedToken() - { - $mock = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getCredentials', 'getFirewallName', 'getRoles'])->disableOriginalConstructor()->getMock(); - $mock - ->expects($this->any()) - ->method('getFirewallName') - ->willReturn('key') - ; - - $mock->setAttributes(['foo' => 'bar']); - - return $mock; - } - - protected function getProvider($userChecker = false, $hide = true) - { - if (false === $userChecker) { - $userChecker = $this->createMock(UserCheckerInterface::class); - } - - return $this->getMockForAbstractClass(UserAuthenticationProvider::class, [$userChecker, 'key', $hide]); - } -} diff --git a/Tests/Authentication/RememberMe/PersistentTokenTest.php b/Tests/Authentication/RememberMe/PersistentTokenTest.php index 9df545a4..1b05dc41 100644 --- a/Tests/Authentication/RememberMe/PersistentTokenTest.php +++ b/Tests/Authentication/RememberMe/PersistentTokenTest.php @@ -12,13 +12,10 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\RememberMe; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; class PersistentTokenTest extends TestCase { - use ExpectDeprecationTrait; - public function testConstructor() { $lastUsed = new \DateTime(); @@ -30,15 +27,4 @@ public function testConstructor() $this->assertEquals('footokenvalue', $token->getTokenValue()); $this->assertSame($lastUsed, $token->getLastUsed()); } - - /** - * @group legacy - */ - public function testLegacyGetUsername() - { - $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', new \DateTime()); - - $this->expectDeprecation('Since symfony/security-core 5.3: Method "Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken::getUsername()" is deprecated, use getUserIdentifier() instead.'); - $this->assertEquals('fooname', $token->getUsername()); - } } diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index 5bb53d7d..b8e41d45 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -21,48 +21,6 @@ class AbstractTokenTest extends TestCase { use ExpectDeprecationTrait; - /** - * @group legacy - */ - public function testLegacyGetUsername() - { - $token = new ConcreteToken(['ROLE_FOO']); - $token->setUser('fabien'); - $this->assertEquals('fabien', $token->getUsername()); - - $token->setUser(new TestUser('fabien')); - $this->assertEquals('fabien', $token->getUsername()); - - $legacyUser = new class() implements UserInterface { - public function getUsername() - { - return 'fabien'; - } - - public function getRoles() - { - return []; - } - - public function getPassword() - { - } - - public function getSalt() - { - } - - public function eraseCredentials() - { - } - }; - $token->setUser($legacyUser); - $this->assertEquals('fabien', $token->getUsername()); - - $token->setUser($legacyUser); - $this->assertEquals('fabien', $token->getUserIdentifier()); - } - /** * @dataProvider provideUsers */ @@ -78,25 +36,6 @@ public function provideUsers() yield [new InMemoryUser('fabien', null), 'fabien']; } - /** - * @dataProvider provideLegacyUsers - * @group legacy - */ - public function testLegacyGetUserIdentifier($user, string $username) - { - $token = new ConcreteToken(['ROLE_FOO']); - $token->setUser($user); - $this->assertEquals($username, $token->getUserIdentifier()); - } - - public function provideLegacyUsers() - { - return [ - [new TestUser('fabien'), 'fabien'], - ['fabien', 'fabien'], - ]; - } - public function testEraseCredentials() { $token = new ConcreteToken(['ROLE_FOO']); @@ -125,21 +64,6 @@ public function testConstructor() $this->assertEquals(['ROLE_FOO'], $token->getRoleNames()); } - /** - * @group legacy - */ - public function testAuthenticatedFlag() - { - $token = new ConcreteToken(); - $this->assertFalse($token->isAuthenticated()); - - $token->setAuthenticated(true); - $this->assertTrue($token->isAuthenticated()); - - $token->setAuthenticated(false); - $this->assertFalse($token->isAuthenticated()); - } - public function testAttributes() { $attributes = ['foo' => 'bar']; @@ -171,75 +95,6 @@ public function testSetUser($user) $token->setUser($user); $this->assertSame($user, $token->getUser()); } - - /** - * @group legacy - * @dataProvider getUserChanges - */ - public function testSetUserSetsAuthenticatedToFalseWhenUserChanges($firstUser, $secondUser) - { - $token = new ConcreteToken(); - $token->setAuthenticated(true); - $this->assertTrue($token->isAuthenticated()); - - $token->setUser($firstUser); - $this->assertTrue($token->isAuthenticated()); - - $token->setUser($secondUser); - $this->assertFalse($token->isAuthenticated()); - } - - public function getUserChanges() - { - $user = $this->createMock(UserInterface::class); - - return [ - ['foo', 'bar'], - ['foo', new TestUser('bar')], - ['foo', $user], - [$user, 'foo'], - [$user, new TestUser('foo')], - [new TestUser('foo'), new TestUser('bar')], - [new TestUser('foo'), 'bar'], - [new TestUser('foo'), $user], - ]; - } - - /** - * @group legacy - * @dataProvider provideUsers - * @dataProvider provideLegacyUsers - */ - public function testSetUserDoesNotSetAuthenticatedToFalseWhenUserDoesNotChange($user) - { - $token = new ConcreteToken(); - $token->setAuthenticated(true); - $this->assertTrue($token->isAuthenticated()); - - $token->setUser($user); - $this->assertTrue($token->isAuthenticated()); - - $token->setUser($user); - $this->assertTrue($token->isAuthenticated()); - } - - /** - * @group legacy - */ - public function testIsUserChangedWhenSerializing() - { - $token = new ConcreteToken(['ROLE_ADMIN']); - $token->setAuthenticated(true); - $this->assertTrue($token->isAuthenticated()); - - $user = new SerializableUser('wouter', ['ROLE_ADMIN']); - $token->setUser($user); - $this->assertTrue($token->isAuthenticated()); - - $token = unserialize(serialize($token)); - $token->setUser($user); - $this->assertTrue($token->isAuthenticated()); - } } class TestUser diff --git a/Tests/Authentication/Token/AnonymousTokenTest.php b/Tests/Authentication/Token/AnonymousTokenTest.php deleted file mode 100644 index 678b2d76..00000000 --- a/Tests/Authentication/Token/AnonymousTokenTest.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * 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\Authentication\Token; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; - -/** - * @group legacy - */ -class AnonymousTokenTest extends TestCase -{ - public function testConstructor() - { - $token = new AnonymousToken('foo', 'bar', ['ROLE_FOO']); - $this->assertEquals(['ROLE_FOO'], $token->getRoleNames()); - } - - public function testIsAuthenticated() - { - $token = new AnonymousToken('foo', 'bar'); - $this->assertTrue($token->isAuthenticated()); - } - - public function testGetKey() - { - $token = new AnonymousToken('foo', 'bar'); - $this->assertEquals('foo', $token->getSecret()); - } - - public function testGetCredentials() - { - $token = new AnonymousToken('foo', 'bar'); - $this->assertEquals('', $token->getCredentials()); - } - - public function testGetUser() - { - $token = new AnonymousToken('foo', 'bar'); - $this->assertEquals('bar', $token->getUser()); - } -} diff --git a/Tests/Authentication/Token/PreAuthenticatedTokenTest.php b/Tests/Authentication/Token/PreAuthenticatedTokenTest.php index e6da2964..dbbb5f70 100644 --- a/Tests/Authentication/Token/PreAuthenticatedTokenTest.php +++ b/Tests/Authentication/Token/PreAuthenticatedTokenTest.php @@ -24,47 +24,9 @@ public function testConstructor() $this->assertEquals('key', $token->getFirewallName()); } - /** - * @group legacy - */ - public function testLegacyConstructor() - { - $token = new PreAuthenticatedToken('foo', 'bar', 'key', ['ROLE_FOO']); - $this->assertEquals(['ROLE_FOO'], $token->getRoleNames()); - $this->assertEquals('key', $token->getFirewallName()); - } - - /** - * @group legacy - */ - public function testGetCredentials() - { - $token = new PreAuthenticatedToken('foo', 'bar', 'key'); - $this->assertEquals('bar', $token->getCredentials()); - } - public function testGetUser() { $token = new PreAuthenticatedToken($user = new InMemoryUser('foo', 'bar'), 'key'); $this->assertEquals($user, $token->getUser()); } - - /** - * @group legacy - */ - public function testEraseCredentials() - { - $token = new PreAuthenticatedToken('foo', 'bar', 'key'); - $token->eraseCredentials(); - $this->assertNull($token->getCredentials()); - } - - /** - * @group legacy - */ - public function testIsAuthenticated() - { - $token = new PreAuthenticatedToken('foo', 'bar', 'key'); - $this->assertFalse($token->isAuthenticated()); - } } diff --git a/Tests/Authentication/Token/RememberMeTokenTest.php b/Tests/Authentication/Token/RememberMeTokenTest.php index 42df2337..a63d481b 100644 --- a/Tests/Authentication/Token/RememberMeTokenTest.php +++ b/Tests/Authentication/Token/RememberMeTokenTest.php @@ -28,16 +28,6 @@ public function testConstructor() $this->assertSame($user, $token->getUser()); } - /** - * @group legacy - */ - public function testIsAuthenticated() - { - $user = $this->getUser(); - $token = new RememberMeToken($user, 'fookey', 'foo'); - $this->assertTrue($token->isAuthenticated()); - } - public function testConstructorSecretCannotBeEmptyString() { $this->expectException(\InvalidArgumentException::class); diff --git a/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php b/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php index 0d074bd4..1d28cb2c 100644 --- a/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php +++ b/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -24,8 +23,6 @@ class UsageTrackingTokenStorageTest extends TestCase { - use ExpectDeprecationTrait; - public function testGetSetToken() { $sessionAccess = 0; @@ -68,22 +65,4 @@ public function testGetSetToken() $this->assertSame($token, $trackingStorage->getToken()); $this->assertSame(1, $sessionAccess); } - - /** - * @group legacy - */ - public function testWithoutMainRequest() - { - $locator = new class(['request_stack' => function () { - return new RequestStack(); - }]) implements ContainerInterface { - use ServiceLocatorTrait; - }; - $tokenStorage = new TokenStorage(); - $trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $locator); - $trackingStorage->enableUsageTracking(); - - $this->expectDeprecation('Since symfony/security-core 5.3: Using "%s" (service ID: "security.token_storage") outside the request-response cycle is deprecated, use the "%s" class (service ID: "security.untracked_token_storage") instead or disable usage tracking using "disableUsageTracking()".'); - $trackingStorage->getToken(); - } } diff --git a/Tests/Authentication/Token/SwitchUserTokenTest.php b/Tests/Authentication/Token/SwitchUserTokenTest.php index ba7fdceb..a6d29a89 100644 --- a/Tests/Authentication/Token/SwitchUserTokenTest.php +++ b/Tests/Authentication/Token/SwitchUserTokenTest.php @@ -41,74 +41,6 @@ public function testSerialize() $this->assertEquals(['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'], $unserializedOriginalToken->getRoleNames()); } - /** - * @group legacy - */ - public function testLegacySerialize() - { - $originalToken = new UsernamePasswordToken('user', 'foo', 'provider-key', ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH']); - $token = new SwitchUserToken('admin', 'bar', 'provider-key', ['ROLE_USER'], $originalToken, 'https://symfony.com/blog'); - - $unserializedToken = unserialize(serialize($token)); - - $this->assertInstanceOf(SwitchUserToken::class, $unserializedToken); - $this->assertSame('admin', $unserializedToken->getUserIdentifier()); - $this->assertSame('bar', $unserializedToken->getCredentials()); - $this->assertSame('provider-key', $unserializedToken->getFirewallName()); - $this->assertEquals(['ROLE_USER'], $unserializedToken->getRoleNames()); - $this->assertSame('https://symfony.com/blog', $unserializedToken->getOriginatedFromUri()); - - $unserializedOriginalToken = $unserializedToken->getOriginalToken(); - - $this->assertInstanceOf(UsernamePasswordToken::class, $unserializedOriginalToken); - $this->assertSame('user', $unserializedOriginalToken->getUserIdentifier()); - $this->assertSame('foo', $unserializedOriginalToken->getCredentials()); - $this->assertSame('provider-key', $unserializedOriginalToken->getFirewallName()); - $this->assertEquals(['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'], $unserializedOriginalToken->getRoleNames()); - } - - /** - * @group legacy - */ - public function testSetUserDoesNotDeauthenticate() - { - $impersonated = new class() implements UserInterface { - public function getUsername() - { - return 'impersonated'; - } - - public function getUserIdentifier() - { - return 'impersonated'; - } - - public function getPassword() - { - return null; - } - - public function eraseCredentials() - { - } - - public function getRoles() - { - return ['ROLE_USER']; - } - - public function getSalt() - { - return null; - } - }; - - $originalToken = new UsernamePasswordToken(new InMemoryUser('impersonator', '', ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH']), 'foo', 'provider-key', ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH']); - $token = new SwitchUserToken($impersonated, 'bar', 'provider-key', ['ROLE_USER', 'ROLE_PREVIOUS_ADMIN'], $originalToken); - $token->setUser($impersonated); - $this->assertTrue($token->isAuthenticated()); - } - public function testSerializeNullImpersonateUrl() { $originalToken = new UsernamePasswordToken(new InMemoryUser('user', 'foo', ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH']), 'provider-key', ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH']); @@ -119,19 +51,6 @@ public function testSerializeNullImpersonateUrl() $this->assertNull($unserializedToken->getOriginatedFromUri()); } - /** - * @group legacy - */ - public function testLegacySerializeNullImpersonateUrl() - { - $originalToken = new UsernamePasswordToken('user', 'foo', 'provider-key', ['ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH']); - $token = new SwitchUserToken('admin', 'bar', 'provider-key', ['ROLE_USER'], $originalToken); - - $unserializedToken = unserialize(serialize($token)); - - $this->assertNull($unserializedToken->getOriginatedFromUri()); - } - /** * Tests if an old version of SwitchUserToken can still be unserialized. * @@ -162,7 +81,6 @@ public function testUnserializeOldToken() self::assertInstanceOf(UsernamePasswordToken::class, $token->getOriginalToken()); self::assertInstanceOf(CustomUser::class, $token->getUser()); self::assertSame('john', $token->getUserIdentifier()); - self::assertSame(['foo' => 'bar'], $token->getCredentials()); self::assertSame('main', $token->getFirewallName()); self::assertEquals(['ROLE_USER'], $token->getRoleNames()); self::assertNull($token->getOriginatedFromUri()); diff --git a/Tests/Authentication/Token/UsernamePasswordTokenTest.php b/Tests/Authentication/Token/UsernamePasswordTokenTest.php index 28b1a61f..91faed0c 100644 --- a/Tests/Authentication/Token/UsernamePasswordTokenTest.php +++ b/Tests/Authentication/Token/UsernamePasswordTokenTest.php @@ -24,62 +24,9 @@ public function testConstructor() $this->assertEquals('key', $token->getFirewallName()); } - /** - * @group legacy - */ - public function testLegacyConstructor() - { - $token = new UsernamePasswordToken('foo', 'bar', 'key', ['ROLE_FOO']); - $this->assertEquals(['ROLE_FOO'], $token->getRoleNames()); - $this->assertEquals('bar', $token->getCredentials()); - $this->assertEquals('key', $token->getFirewallName()); - } - - /** - * @group legacy - */ - public function testIsAuthenticated() - { - $token = new UsernamePasswordToken('foo', 'bar', 'key'); - $this->assertFalse($token->isAuthenticated()); - - $token = new UsernamePasswordToken('foo', 'bar', 'key', ['ROLE_FOO']); - $this->assertTrue($token->isAuthenticated()); - } - - /** - * @group legacy - */ - public function testSetAuthenticatedToTrue() - { - $this->expectException(\LogicException::class); - $token = new UsernamePasswordToken('foo', 'bar', 'key'); - $token->setAuthenticated(true); - } - - /** - * @group legacy - */ - public function testSetAuthenticatedToFalse() - { - $token = new UsernamePasswordToken('foo', 'bar', 'key'); - $token->setAuthenticated(false); - $this->assertFalse($token->isAuthenticated()); - } - - /** - * @group legacy - */ - public function testEraseCredentials() - { - $token = new UsernamePasswordToken('foo', 'bar', 'key'); - $token->eraseCredentials(); - $this->assertEquals('', $token->getCredentials()); - } - public function testToString() { $token = new UsernamePasswordToken(new InMemoryUser('foo', '', ['A', 'B']), 'foo', ['A', 'B']); - $this->assertEquals('UsernamePasswordToken(user="foo", authenticated=true, roles="A, B")', (string) $token); + $this->assertEquals('UsernamePasswordToken(user="foo", roles="A, B")', (string) $token); } } diff --git a/Tests/Authorization/AccessDecisionManagerTest.php b/Tests/Authorization/AccessDecisionManagerTest.php index 375fb6d6..b86811bb 100644 --- a/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Tests/Authorization/AccessDecisionManagerTest.php @@ -38,20 +38,6 @@ public function testStrategies($strategy, $voters, $allowIfAllAbstainDecisions, $this->assertSame($expected, $manager->decide($token, ['ROLE_FOO'])); } - /** - * @dataProvider provideStrategies - * @group legacy - */ - public function testDeprecatedVoter($strategy) - { - $token = $this->createMock(TokenInterface::class); - $manager = new AccessDecisionManager([$this->getVoter(3)], $strategy); - - $this->expectDeprecation('Since symfony/security-core 5.3: Returning "3" in "%s::vote()" is deprecated, return one of "Symfony\Component\Security\Core\Authorization\Voter\VoterInterface" constants: "ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN".'); - - $manager->decide($token, ['ROLE_FOO']); - } - public function getStrategyTests() { return [ @@ -112,14 +98,6 @@ public function getStrategyTests() ]; } - public function provideStrategies() - { - yield [AccessDecisionManager::STRATEGY_AFFIRMATIVE]; - yield [AccessDecisionManager::STRATEGY_CONSENSUS]; - yield [AccessDecisionManager::STRATEGY_UNANIMOUS]; - yield [AccessDecisionManager::STRATEGY_PRIORITY]; - } - protected function getVoters($grants, $denies, $abstains) { $voters = []; diff --git a/Tests/Authorization/AuthorizationCheckerTest.php b/Tests/Authorization/AuthorizationCheckerTest.php index 160b921b..a3dd46a1 100644 --- a/Tests/Authorization/AuthorizationCheckerTest.php +++ b/Tests/Authorization/AuthorizationCheckerTest.php @@ -23,7 +23,6 @@ class AuthorizationCheckerTest extends TestCase { - private $authenticationManager; private $accessDecisionManager; private $authorizationChecker; private $tokenStorage; @@ -33,75 +32,18 @@ protected function setUp(): void $this->accessDecisionManager = $this->createMock(AccessDecisionManagerInterface::class); $this->tokenStorage = new TokenStorage(); - $this->authorizationChecker = new AuthorizationChecker( - $this->tokenStorage, - $this->accessDecisionManager, - false, - false - ); + $this->authorizationChecker = new AuthorizationChecker($this->tokenStorage, $this->accessDecisionManager); } - /** - * @group legacy - */ - public function testVoteAuthenticatesTokenIfNecessary() - { - $token = new UsernamePasswordToken('username', 'password', 'provider'); - $this->tokenStorage->setToken($token); - - $newToken = new UsernamePasswordToken('username', 'password', 'provider'); - - $authenticationManager = $this->createMock(AuthenticationManagerInterface::class); - $this->authorizationChecker = new AuthorizationChecker($this->tokenStorage, $authenticationManager, $this->accessDecisionManager, false, false); - $authenticationManager - ->expects($this->once()) - ->method('authenticate') - ->with($this->equalTo($token)) - ->willReturn($newToken); - - // default with() isn't a strict check - $tokenComparison = function ($value) use ($newToken) { - // make sure that the new token is used in "decide()" and not the old one - return $value === $newToken; - }; - - $this->accessDecisionManager - ->expects($this->once()) - ->method('decide') - ->with($this->callback($tokenComparison)) - ->willReturn(true); - - // first run the token has not been re-authenticated yet, after isGranted is called, it should be equal - $this->assertNotSame($newToken, $this->tokenStorage->getToken()); - $this->assertTrue($this->authorizationChecker->isGranted('foo')); - $this->assertSame($newToken, $this->tokenStorage->getToken()); - } - - /** - * @group legacy - */ - public function testLegacyVoteWithoutAuthenticationToken() + public function testVoteWithoutAuthenticationToken() { $authorizationChecker = new AuthorizationChecker($this->tokenStorage, $this->accessDecisionManager); - $this->expectException(AuthenticationCredentialsNotFoundException::class); + $this->accessDecisionManager->expects($this->once())->method('decide')->with($this->isInstanceOf(NullToken::class))->willReturn(false); $authorizationChecker->isGranted('ROLE_FOO'); } - public function testVoteWithoutAuthenticationToken() - { - $authorizationChecker = new AuthorizationChecker($this->tokenStorage, $this->accessDecisionManager, false, false); - - $this->accessDecisionManager - ->expects($this->once()) - ->method('decide') - ->with($this->isInstanceOf(NullToken::class)) - ->willReturn(true); - - $this->assertTrue($authorizationChecker->isGranted('ANONYMOUS')); - } - /** * @dataProvider isGrantedProvider */ diff --git a/Tests/Authorization/ExpressionLanguageTest.php b/Tests/Authorization/ExpressionLanguageTest.php index c76ca77d..e1366732 100644 --- a/Tests/Authorization/ExpressionLanguageTest.php +++ b/Tests/Authorization/ExpressionLanguageTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; @@ -36,7 +35,7 @@ public function testIsAuthenticated($token, $expression, $result) $tokenStorage = new TokenStorage(); $tokenStorage->setToken($token); $accessDecisionManager = new AccessDecisionManager([new RoleVoter(), new AuthenticatedVoter($trustResolver)]); - $authChecker = new AuthorizationChecker($tokenStorage, $accessDecisionManager, false, false); + $authChecker = new AuthorizationChecker($tokenStorage, $accessDecisionManager); $context = []; $context['auth_checker'] = $authChecker; @@ -72,35 +71,4 @@ public function provider() [$usernamePasswordToken, "is_granted('ROLE_USER')", true], ]; } - - /** - * @dataProvider legacyProvider - * @group legacy - */ - public function testLegacyIsAuthenticated($token, $expression, $result) - { - $this->testIsAuthenticated($token, $expression, $result); - } - - /** - * @group legacy - */ - public function legacyProvider() - { - $roles = ['ROLE_USER', 'ROLE_ADMIN']; - $user = new InMemoryUser('username', 'password', $roles); - $anonymousToken = new AnonymousToken('firewall', 'anon.'); - - return [ - [$anonymousToken, 'is_anonymous()', true], - [$anonymousToken, 'is_authenticated()', false], - [$anonymousToken, 'is_fully_authenticated()', false], - [$anonymousToken, 'is_remember_me()', false], - [$anonymousToken, "is_granted('ROLE_USER')", false], - - [null, 'is_anonymous()', false], - [new RememberMeToken($user, 'firewall-name', 'firewall'), 'is_anonymous()', false], - [new UsernamePasswordToken($user, 'firewall-name', $roles), 'is_anonymous()', false], - ]; - } } diff --git a/Tests/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Authorization/Voter/AuthenticatedVoterTest.php index c2de6c1f..346568c2 100644 --- a/Tests/Authorization/Voter/AuthenticatedVoterTest.php +++ b/Tests/Authorization/Voter/AuthenticatedVoterTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -52,36 +51,6 @@ public function getVoteTests() ]; } - /** - * @group legacy - * @dataProvider getLegacyVoteTests - */ - public function testLegacyVote($authenticated, $attributes, $expected) - { - $this->testVote($authenticated, $attributes, $expected); - } - - public function getLegacyVoteTests() - { - return [ - ['anonymously', [], VoterInterface::ACCESS_ABSTAIN], - ['anonymously', ['FOO'], VoterInterface::ACCESS_ABSTAIN], - ['anonymously', ['IS_AUTHENTICATED_ANONYMOUSLY'], VoterInterface::ACCESS_GRANTED], - ['anonymously', ['IS_AUTHENTICATED_REMEMBERED'], VoterInterface::ACCESS_DENIED], - ['anonymously', ['IS_AUTHENTICATED_FULLY'], VoterInterface::ACCESS_DENIED], - ['anonymously', ['IS_ANONYMOUS'], VoterInterface::ACCESS_GRANTED], - ['anonymously', ['IS_IMPERSONATOR'], VoterInterface::ACCESS_DENIED], - - ['fully', ['IS_ANONYMOUS'], VoterInterface::ACCESS_DENIED], - ['remembered', ['IS_ANONYMOUS'], VoterInterface::ACCESS_DENIED], - ['anonymously', ['IS_ANONYMOUS'], VoterInterface::ACCESS_GRANTED], - - ['fully', ['IS_AUTHENTICATED_ANONYMOUSLY'], VoterInterface::ACCESS_GRANTED], - ['remembered', ['IS_AUTHENTICATED_ANONYMOUSLY'], VoterInterface::ACCESS_GRANTED], - ['anonymously', ['IS_AUTHENTICATED_ANONYMOUSLY'], VoterInterface::ACCESS_GRANTED], - ]; - } - protected function getToken($authenticated) { if ('fully' === $authenticated) { @@ -90,8 +59,6 @@ protected function getToken($authenticated) return $this->getMockBuilder(RememberMeToken::class)->setMethods(['setPersistent'])->disableOriginalConstructor()->getMock(); } elseif ('impersonated' === $authenticated) { return $this->getMockBuilder(SwitchUserToken::class)->disableOriginalConstructor()->getMock(); - } else { - return $this->getMockBuilder(AnonymousToken::class)->setConstructorArgs(['', ''])->getMock(); } } } diff --git a/Tests/Authorization/Voter/RoleVoterTest.php b/Tests/Authorization/Voter/RoleVoterTest.php index 43f80248..c0a13895 100644 --- a/Tests/Authorization/Voter/RoleVoterTest.php +++ b/Tests/Authorization/Voter/RoleVoterTest.php @@ -47,17 +47,6 @@ public function getVoteTests() ]; } - /** - * @group legacy - */ - public function testDeprecatedRolePreviousAdmin() - { - $this->expectDeprecation('Since symfony/security-core 5.1: The ROLE_PREVIOUS_ADMIN role is deprecated and will be removed in version 6.0, use the IS_IMPERSONATOR attribute instead.'); - $voter = new RoleVoter(); - - $voter->vote($this->getTokenWithRoleNames(['ROLE_USER', 'ROLE_PREVIOUS_ADMIN']), null, ['ROLE_PREVIOUS_ADMIN']); - } - protected function getTokenWithRoleNames(array $roles) { $token = $this->createMock(AbstractToken::class); diff --git a/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php b/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php index 2ed70850..a081034a 100644 --- a/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php +++ b/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php @@ -44,7 +44,7 @@ public function testConstructWithSAfeMessage() public function testSharedSerializedData() { - $token = new UsernamePasswordToken(new InMemoryUser('foo', ''), 'bar'); + $token = new UsernamePasswordToken(new InMemoryUser('foo', 'bar', ['ROLE_USER']), 'main', ['ROLE_USER']); $exception = new CustomUserMessageAuthenticationException(); $exception->setToken($token); @@ -58,7 +58,7 @@ public function testSharedSerializedData() public function testSharedSerializedDataFromChild() { - $token = new UsernamePasswordToken(new InMemoryUser('foo', ''), 'bar'); + $token = new UsernamePasswordToken(new InMemoryUser('foo', 'bar', ['ROLE_USER']), 'main', ['ROLE_USER']); $exception = new ChildCustomUserMessageAuthenticationException(); $exception->childMember = $token; diff --git a/Tests/Exception/UserNotFoundExceptionTest.php b/Tests/Exception/UserNotFoundExceptionTest.php index 3d9de8f1..f526b37f 100644 --- a/Tests/Exception/UserNotFoundExceptionTest.php +++ b/Tests/Exception/UserNotFoundExceptionTest.php @@ -31,26 +31,4 @@ public function testUserIdentifierIsNotSetByDefault() $this->assertNull($exception->getUserIdentifier()); } - - /** - * @group legacy - */ - public function testUsernameIsNotSetByDefault() - { - $exception = new UserNotFoundException(); - - $this->assertNull($exception->getUsername()); - } - - /** - * @group legacy - */ - public function testUsernameNotFoundException() - { - $exception = new UsernameNotFoundException(); - $this->assertInstanceOf(UserNotFoundException::class, $exception); - - $exception->setUsername('username'); - $this->assertEquals('username', $exception->getUserIdentifier()); - } } diff --git a/Tests/SecurityTest.php b/Tests/SecurityTest.php index e1398fa4..53dd23b2 100644 --- a/Tests/SecurityTest.php +++ b/Tests/SecurityTest.php @@ -39,7 +39,6 @@ public function testGetToken() /** * @dataProvider getUserTests - * @dataProvider getLegacyUserTests */ public function testGetUser($userInToken, $expectedUser) { @@ -67,16 +66,6 @@ public function getUserTests() yield [$user, $user]; } - /** - * @group legacy - */ - public function getLegacyUserTests() - { - yield ['string_username', null]; - - yield [new StringishUser(), null]; - } - public function testIsGranted() { $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); diff --git a/Tests/User/InMemoryUserProviderTest.php b/Tests/User/InMemoryUserProviderTest.php index d4d4964c..aa058bba 100644 --- a/Tests/User/InMemoryUserProviderTest.php +++ b/Tests/User/InMemoryUserProviderTest.php @@ -44,22 +44,6 @@ public function testRefresh() $this->assertFalse($refreshedUser->isEnabled()); } - /** - * @group legacy - */ - public function testRefreshWithLegacyUser() - { - $user = new User('fabien', 'bar'); - - $provider = $this->createProvider(); - - $refreshedUser = $provider->refreshUser($user); - $this->assertEquals('foo', $refreshedUser->getPassword()); - $this->assertEquals(['ROLE_USER'], $refreshedUser->getRoles()); - $this->assertFalse($refreshedUser->isEnabled()); - $this->assertFalse($refreshedUser->isCredentialsNonExpired()); - } - protected function createProvider(): InMemoryUserProvider { return new InMemoryUserProvider([ diff --git a/Tests/User/InMemoryUserTest.php b/Tests/User/InMemoryUserTest.php index a5496ef3..fb803747 100644 --- a/Tests/User/InMemoryUserTest.php +++ b/Tests/User/InMemoryUserTest.php @@ -42,29 +42,12 @@ public function testGetPassword() $this->assertEquals('superpass', $user->getPassword()); } - /** - * @group legacy - */ - public function testGetUsername() - { - $user = new InMemoryUser('fabien', 'superpass'); - - $this->expectDeprecation('Since symfony/security-core 5.3: Method "Symfony\Component\Security\Core\User\User::getUsername()" is deprecated, use getUserIdentifier() instead.'); - $this->assertEquals('fabien', $user->getUsername()); - } - public function testGetUserIdentifier() { $user = new InMemoryUser('fabien', 'superpass'); $this->assertEquals('fabien', $user->getUserIdentifier()); } - public function testGetSalt() - { - $user = new InMemoryUser('fabien', 'superpass'); - $this->assertNull($user->getSalt()); - } - public function testIsEnabled() { $user = new InMemoryUser('mathilde', 'k'); @@ -90,9 +73,9 @@ public function testToString() /** * @dataProvider isEqualToData * - * @param bool $expectation - * @param EquatableInterface|UserInterface $a - * @param EquatableInterface|UserInterface $b + * @param bool $expectation + * @param UserInterface $a + * @param UserInterface $b */ public function testIsEqualTo($expectation, $a, $b) { diff --git a/Tests/User/UserCheckerTest.php b/Tests/User/UserCheckerTest.php deleted file mode 100644 index 728d935b..00000000 --- a/Tests/User/UserCheckerTest.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * 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\User; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Exception\AccountExpiredException; -use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; -use Symfony\Component\Security\Core\Exception\DisabledException; -use Symfony\Component\Security\Core\Exception\LockedException; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserChecker; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class UserCheckerTest extends TestCase -{ - public function testCheckPostAuthNotAdvancedUserInterface() - { - $checker = new UserChecker(); - - $this->assertNull($checker->checkPostAuth($this->createMock(UserInterface::class))); - } - - public function testCheckPostAuthPass() - { - $checker = new UserChecker(); - $this->assertNull($checker->checkPostAuth(new User('John', 'password'))); - } - - public function testCheckPostAuthCredentialsExpired() - { - $this->expectException(CredentialsExpiredException::class); - $checker = new UserChecker(); - $checker->checkPostAuth(new User('John', 'password', [], true, true, false, true)); - } - - public function testCheckPreAuthAccountLocked() - { - $this->expectException(LockedException::class); - $checker = new UserChecker(); - $checker->checkPreAuth(new User('John', 'password', [], true, true, false, false)); - } - - public function testCheckPreAuthDisabled() - { - $this->expectException(DisabledException::class); - $checker = new UserChecker(); - $checker->checkPreAuth(new User('John', 'password', [], false, true, false, true)); - } - - public function testCheckPreAuthAccountExpired() - { - $this->expectException(AccountExpiredException::class); - $checker = new UserChecker(); - $checker->checkPreAuth(new User('John', 'password', [], true, false, true, true)); - } -} diff --git a/Tests/User/UserTest.php b/Tests/User/UserTest.php deleted file mode 100644 index 81b8705d..00000000 --- a/Tests/User/UserTest.php +++ /dev/null @@ -1,152 +0,0 @@ - - * - * 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\User; - -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Component\Security\Core\User\EquatableInterface; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * @group legacy - */ -class UserTest extends TestCase -{ - use ExpectDeprecationTrait; - - public function testConstructorException() - { - $this->expectException(\InvalidArgumentException::class); - new User('', 'superpass'); - } - - public function testGetRoles() - { - $user = new User('fabien', 'superpass'); - $this->assertEquals([], $user->getRoles()); - - $user = new User('fabien', 'superpass', ['ROLE_ADMIN']); - $this->assertEquals(['ROLE_ADMIN'], $user->getRoles()); - } - - public function testGetPassword() - { - $user = new User('fabien', 'superpass'); - $this->assertEquals('superpass', $user->getPassword()); - } - - /** - * @group legacy - */ - public function testGetUsername() - { - $user = new User('fabien', 'superpass'); - - $this->expectDeprecation('Since symfony/security-core 5.3: Method "Symfony\Component\Security\Core\User\User::getUsername()" is deprecated, use getUserIdentifier() instead.'); - $this->assertEquals('fabien', $user->getUsername()); - } - - public function testGetUserIdentifier() - { - $user = new User('fabien', 'superpass'); - $this->assertEquals('fabien', $user->getUserIdentifier()); - } - - public function testGetSalt() - { - $user = new User('fabien', 'superpass'); - $this->assertEquals('', $user->getSalt()); - } - - public function testIsAccountNonExpired() - { - $user = new User('fabien', 'superpass'); - $this->assertTrue($user->isAccountNonExpired()); - - $user = new User('fabien', 'superpass', [], true, false); - $this->assertFalse($user->isAccountNonExpired()); - } - - public function testIsCredentialsNonExpired() - { - $user = new User('fabien', 'superpass'); - $this->assertTrue($user->isCredentialsNonExpired()); - - $user = new User('fabien', 'superpass', [], true, true, false); - $this->assertFalse($user->isCredentialsNonExpired()); - } - - public function testIsAccountNonLocked() - { - $user = new User('fabien', 'superpass'); - $this->assertTrue($user->isAccountNonLocked()); - - $user = new User('fabien', 'superpass', [], true, true, true, false); - $this->assertFalse($user->isAccountNonLocked()); - } - - public function testIsEnabled() - { - $user = new User('fabien', 'superpass'); - $this->assertTrue($user->isEnabled()); - - $user = new User('fabien', 'superpass', [], false); - $this->assertFalse($user->isEnabled()); - } - - public function testEraseCredentials() - { - $user = new User('fabien', 'superpass'); - $user->eraseCredentials(); - $this->assertEquals('superpass', $user->getPassword()); - } - - public function testToString() - { - $user = new User('fabien', 'superpass'); - $this->assertEquals('fabien', (string) $user); - } - - /** - * @dataProvider isEqualToData - * - * @param bool $expectation - * @param EquatableInterface|UserInterface $a - * @param EquatableInterface|UserInterface $b - */ - public function testIsEqualTo($expectation, $a, $b) - { - $this->assertSame($expectation, $a->isEqualTo($b)); - $this->assertSame($expectation, $b->isEqualTo($a)); - } - - public static function isEqualToData() - { - return [ - [true, new User('username', 'password'), new User('username', 'password')], - [false, new User('username', 'password', ['ROLE']), new User('username', 'password')], - [false, new User('username', 'password', ['ROLE']), new User('username', 'password', ['NO ROLE'])], - [false, new User('diff', 'diff'), new User('username', 'password')], - [false, new User('diff', 'diff', [], false), new User('username', 'password')], - [false, new User('diff', 'diff', [], false, false), new User('username', 'password')], - [false, new User('diff', 'diff', [], false, false, false), new User('username', 'password')], - [false, new User('diff', 'diff', [], false, false, false, false), new User('username', 'password')], - ]; - } - - public function testIsEqualToWithDifferentUser() - { - $user = new User('username', 'password'); - $this->assertFalse($user->isEqualTo($this->createMock(UserInterface::class))); - } -} diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 8ef3a5a0..6a6f2833 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -47,12 +47,10 @@ public function getProviders() } /** - * {@inheritdoc} + * @internal for compatibility with Symfony 5.4 */ public function loadUserByUsername(string $username) { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); - return $this->loadUserByIdentifier($username); } @@ -60,13 +58,6 @@ public function loadUserByIdentifier(string $identifier): UserInterface { foreach ($this->providers as $provider) { try { - // @deprecated since Symfony 5.3, change to $provider->loadUserByIdentifier() in 6.0 - if (!method_exists($provider, 'loadUserByIdentifier')) { - trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($provider)); - - return $provider->loadUserByUsername($identifier); - } - return $provider->loadUserByIdentifier($identifier); } catch (UserNotFoundException $e) { // try next one @@ -101,8 +92,7 @@ public function refreshUser(UserInterface $user) } if ($supportedUserFound) { - // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 - $username = method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(); + $username = $user->getUserIdentifier(); $e = new UserNotFoundException(sprintf('There is no user with name "%s".', $username)); $e->setUserIdentifier($username); throw $e; diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index 39da71e3..c1595955 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -19,58 +19,104 @@ * @author Robin Chalas * @author Fabien Potencier */ -final class InMemoryUser extends User +final class InMemoryUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface { + private $username; + private $password; + private $enabled; + private $roles; + + public function __construct(?string $username, ?string $password, array $roles = [], bool $enabled = true) + { + if ('' === $username || null === $username) { + throw new \InvalidArgumentException('The username cannot be empty.'); + } + + $this->username = $username; + $this->password = $password; + $this->enabled = $enabled; + $this->roles = $roles; + } + + public function __toString(): string + { + return $this->getUserIdentifier(); + } + /** * {@inheritdoc} - * - * @deprecated since Symfony 5.3 */ - public function isAccountNonExpired(): bool + public function getRoles(): array { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, you should stop using it.', __METHOD__); - - return parent::isAccountNonExpired(); + return $this->roles; } /** * {@inheritdoc} - * - * @deprecated since Symfony 5.3 */ - public function isAccountNonLocked(): bool + public function getPassword(): ?string { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, you should stop using it.', __METHOD__); + return $this->password; + } - return parent::isAccountNonLocked(); + /** + * Returns the identifier for this user (e.g. its username or e-mailaddress). + */ + public function getUserIdentifier(): string + { + return $this->username; } /** - * {@inheritdoc} + * Checks whether the user is enabled. + * + * Internally, if this method returns false, the authentication system + * will throw a DisabledException and prevent login. + * + * @return bool true if the user is enabled, false otherwise * - * @deprecated since Symfony 5.3 + * @see DisabledException */ - public function isCredentialsNonExpired(): bool + public function isEnabled(): bool { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, you should stop using it.', __METHOD__); - - return parent::isCredentialsNonExpired(); + return $this->enabled; } /** - * @deprecated since Symfony 5.3 + * {@inheritdoc} */ - public function getExtraFields(): array + public function eraseCredentials() { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, you should stop using it.', __METHOD__); - - return parent::getExtraFields(); } - public function setPassword(string $password) + /** + * {@inheritdoc} + */ + public function isEqualTo(UserInterface $user): bool { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, you should stop using it.', __METHOD__); + if (!$user instanceof self) { + return false; + } + + if ($this->getPassword() !== $user->getPassword()) { + return false; + } + + $currentRoles = array_map('strval', (array) $this->getRoles()); + $newRoles = array_map('strval', (array) $user->getRoles()); + $rolesChanged = \count($currentRoles) !== \count($newRoles) || \count($currentRoles) !== \count(array_intersect($currentRoles, $newRoles)); + if ($rolesChanged) { + return false; + } + + if ($this->getUserIdentifier() !== $user->getUserIdentifier()) { + return false; + } + + if ($this->isEnabled() !== $user->isEnabled()) { + return false; + } - parent::setPassword($password); + return true; } } diff --git a/User/InMemoryUserChecker.php b/User/InMemoryUserChecker.php index b35e6303..5649cc99 100644 --- a/User/InMemoryUserChecker.php +++ b/User/InMemoryUserChecker.php @@ -25,9 +25,7 @@ class InMemoryUserChecker implements UserCheckerInterface { public function checkPreAuth(UserInterface $user) { - // @deprecated since Symfony 5.3, in 6.0 change to: - // if (!$user instanceof InMemoryUser) { - if (!$user instanceof InMemoryUser && !$user instanceof User) { + if (!$user instanceof InMemoryUser) { return; } @@ -36,38 +34,9 @@ public function checkPreAuth(UserInterface $user) $ex->setUser($user); throw $ex; } - - // @deprecated since Symfony 5.3 - if (User::class === \get_class($user)) { - if (!$user->isAccountNonLocked()) { - $ex = new LockedException('User account is locked.'); - $ex->setUser($user); - throw $ex; - } - - if (!$user->isAccountNonExpired()) { - $ex = new AccountExpiredException('User account has expired.'); - $ex->setUser($user); - throw $ex; - } - } } public function checkPostAuth(UserInterface $user) { - // @deprecated since Symfony 5.3, noop in 6.0 - if (User::class !== \get_class($user)) { - return; - } - - if (!$user->isCredentialsNonExpired()) { - $ex = new CredentialsExpiredException('User credentials have expired.'); - $ex->setUser($user); - throw $ex; - } } } - -if (!class_exists(UserChecker::class, false)) { - class_alias(InMemoryUserChecker::class, UserChecker::class); -} diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index d0779394..a472a6b1 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -51,8 +51,7 @@ public function __construct(array $users = []) */ public function createUser(UserInterface $user) { - // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 - $userIdentifier = strtolower(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername()); + $userIdentifier = strtolower($user->getUserIdentifier()); if (isset($this->users[$userIdentifier])) { throw new \LogicException('Another user with the same username already exists.'); } @@ -60,22 +59,11 @@ public function createUser(UserInterface $user) $this->users[$userIdentifier] = $user; } - /** - * {@inheritdoc} - */ - public function loadUserByUsername(string $username) - { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use loadUserByIdentifier() instead.', __METHOD__); - - return $this->loadUserByIdentifier($username); - } - public function loadUserByIdentifier(string $identifier): UserInterface { $user = $this->getUser($identifier); - // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 - return new InMemoryUser(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $user->getPassword(), $user->getRoles(), $user->isEnabled()); + return new InMemoryUser($user->getUserIdentifier(), $user->getPassword(), $user->getRoles(), $user->isEnabled()); } /** @@ -83,28 +71,12 @@ public function loadUserByIdentifier(string $identifier): UserInterface */ public function refreshUser(UserInterface $user) { - if (!$user instanceof InMemoryUser && !$user instanceof User) { + if (!$user instanceof InMemoryUser) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } - // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 - $storedUser = $this->getUser(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername()); - $userIdentifier = method_exists($storedUser, 'getUserIdentifier') ? $storedUser->getUserIdentifier() : $storedUser->getUsername(); - - // @deprecated since Symfony 5.3 - if (User::class === \get_class($user)) { - if (User::class !== \get_class($storedUser)) { - $accountNonExpired = true; - $credentialsNonExpired = $storedUser->getPassword() === $user->getPassword(); - $accountNonLocked = true; - } else { - $accountNonExpired = $storedUser->isAccountNonExpired(); - $credentialsNonExpired = $storedUser->isCredentialsNonExpired() && $storedUser->getPassword() === $user->getPassword(); - $accountNonLocked = $storedUser->isAccountNonLocked(); - } - - return new User($userIdentifier, $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled(), $accountNonExpired, $credentialsNonExpired, $accountNonLocked); - } + $storedUser = $this->getUser($user->getUserIdentifier()); + $userIdentifier = $storedUser->getUserIdentifier(); return new InMemoryUser($userIdentifier, $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); } @@ -114,11 +86,6 @@ public function refreshUser(UserInterface $user) */ public function supportsClass(string $class) { - // @deprecated since Symfony 5.3 - if (User::class === $class) { - return true; - } - return InMemoryUser::class == $class; } diff --git a/User/User.php b/User/User.php deleted file mode 100644 index f3efb231..00000000 --- a/User/User.php +++ /dev/null @@ -1,218 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\User; - -/** - * User is the user implementation used by the in-memory user provider. - * - * This should not be used for anything else. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use {@link InMemoryUser} instead - */ -class User implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface -{ - private $username; - private $password; - private $enabled; - private $accountNonExpired; - private $credentialsNonExpired; - private $accountNonLocked; - private $roles; - private $extraFields; - - public function __construct(?string $username, ?string $password, array $roles = [], bool $enabled = true, bool $userNonExpired = true, bool $credentialsNonExpired = true, bool $userNonLocked = true, array $extraFields = []) - { - if (InMemoryUser::class !== static::class) { - trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', self::class, InMemoryUser::class); - } - - if ('' === $username || null === $username) { - throw new \InvalidArgumentException('The username cannot be empty.'); - } - - $this->username = $username; - $this->password = $password; - $this->enabled = $enabled; - $this->accountNonExpired = $userNonExpired; - $this->credentialsNonExpired = $credentialsNonExpired; - $this->accountNonLocked = $userNonLocked; - $this->roles = $roles; - $this->extraFields = $extraFields; - } - - public function __toString(): string - { - return $this->getUserIdentifier(); - } - - /** - * {@inheritdoc} - */ - public function getRoles(): array - { - return $this->roles; - } - - /** - * {@inheritdoc} - */ - public function getPassword(): ?string - { - return $this->password; - } - - /** - * {@inheritdoc} - */ - public function getSalt(): ?string - { - return null; - } - - /** - * {@inheritdoc} - */ - public function getUsername(): string - { - trigger_deprecation('symfony/security-core', '5.3', 'Method "%s()" is deprecated, use getUserIdentifier() instead.', __METHOD__); - - return $this->username; - } - - /** - * Returns the identifier for this user (e.g. its username or e-mailaddress). - */ - public function getUserIdentifier(): string - { - return $this->username; - } - - /** - * Checks whether the user's account has expired. - * - * Internally, if this method returns false, the authentication system - * will throw an AccountExpiredException and prevent login. - * - * @see AccountExpiredException - */ - public function isAccountNonExpired(): bool - { - return $this->accountNonExpired; - } - - /** - * Checks whether the user is locked. - * - * Internally, if this method returns false, the authentication system - * will throw a LockedException and prevent login. - * - * @see LockedException - */ - public function isAccountNonLocked(): bool - { - return $this->accountNonLocked; - } - - /** - * Checks whether the user's credentials (password) has expired. - * - * Internally, if this method returns false, the authentication system - * will throw a CredentialsExpiredException and prevent login. - * - * @see CredentialsExpiredException - */ - public function isCredentialsNonExpired(): bool - { - return $this->credentialsNonExpired; - } - - /** - * Checks whether the user is enabled. - * - * Internally, if this method returns false, the authentication system - * will throw a DisabledException and prevent login. - * - * @see DisabledException - */ - public function isEnabled(): bool - { - return $this->enabled; - } - - /** - * {@inheritdoc} - */ - public function eraseCredentials() - { - } - - public function getExtraFields(): array - { - return $this->extraFields; - } - - /** - * {@inheritdoc} - */ - public function isEqualTo(UserInterface $user): bool - { - if (!$user instanceof self) { - return false; - } - - if ($this->getPassword() !== $user->getPassword()) { - return false; - } - - if ($this->getSalt() !== $user->getSalt()) { - return false; - } - - $currentRoles = array_map('strval', (array) $this->getRoles()); - $newRoles = array_map('strval', (array) $user->getRoles()); - $rolesChanged = \count($currentRoles) !== \count($newRoles) || \count($currentRoles) !== \count(array_intersect($currentRoles, $newRoles)); - if ($rolesChanged) { - return false; - } - - if ($this->getUserIdentifier() !== $user->getUserIdentifier()) { - return false; - } - - if (self::class === static::class) { - if ($this->isAccountNonExpired() !== $user->isAccountNonExpired()) { - return false; - } - - if ($this->isAccountNonLocked() !== $user->isAccountNonLocked()) { - return false; - } - - if ($this->isCredentialsNonExpired() !== $user->isCredentialsNonExpired()) { - return false; - } - } - - if ($this->isEnabled() !== $user->isEnabled()) { - return false; - } - - return true; - } - - public function setPassword(string $password) - { - $this->password = $password; - } -} diff --git a/User/UserChecker.php b/User/UserChecker.php deleted file mode 100644 index 8ffecf17..00000000 --- a/User/UserChecker.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\User; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', UserChecker::class, InMemoryUserChecker::class); - -class_exists(InMemoryUserChecker::class); - -if (false) { - /** - * UserChecker checks the user account flags. - * - * @author Fabien Potencier - * - * @deprecated since Symfony 5.3, use {@link InMemoryUserChecker} instead - */ - class UserChecker - { - } -} diff --git a/User/UserInterface.php b/User/UserInterface.php index e36ed077..e51ff5cf 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -26,8 +26,6 @@ * * @see UserProviderInterface * - * @method string getUserIdentifier() returns the identifier for this user (e.g. its username or e-mailaddress) - * * @author Fabien Potencier */ interface UserInterface @@ -57,9 +55,7 @@ public function getRoles(); public function eraseCredentials(); /** - * @return string - * - * @deprecated since Symfony 5.3, use getUserIdentifier() instead + * Returns the identifier for this user (e.g. its username or e-mailaddress). */ - public function getUsername(); + public function getUserIdentifier(): string; } diff --git a/User/UserProviderInterface.php b/User/UserProviderInterface.php index 2444bdfa..54a167e7 100644 --- a/User/UserProviderInterface.php +++ b/User/UserProviderInterface.php @@ -29,8 +29,7 @@ * * @see UserInterface * - * @method UserInterface loadUserByIdentifier(string $identifier) loads the user for the given user identifier (e.g. username or email). - * This method must throw UserNotFoundException if the user is not found. + * @method UserInterface loadUserByIdentifier(string $identifier) * * @author Fabien Potencier */ @@ -59,11 +58,11 @@ public function refreshUser(UserInterface $user); public function supportsClass(string $class); /** - * @return UserInterface + * Loads the user for the given user identifier (e.g. username or email). * - * @throws UserNotFoundException + * This method must throw UserNotFoundException if the user is not found. * - * @deprecated since Symfony 5.3, use loadUserByIdentifier() instead + * @throws UserNotFoundException */ - public function loadUserByUsername(string $username); + public function loadUserByIdentifier(string $identifier): UserInterface; } From 2f0541dd40f296b6950b4e85fe50d2e6208d1295 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Thu, 19 Aug 2021 12:03:40 +0200 Subject: [PATCH 018/138] [Security] Make `AuthenticationTrustResolverInterface::isAuthenticated()` non-virtual --- Authentication/AuthenticationTrustResolverInterface.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Authentication/AuthenticationTrustResolverInterface.php b/Authentication/AuthenticationTrustResolverInterface.php index 16723d88..39c20547 100644 --- a/Authentication/AuthenticationTrustResolverInterface.php +++ b/Authentication/AuthenticationTrustResolverInterface.php @@ -17,11 +17,14 @@ * Interface for resolving the authentication status of a given token. * * @author Johannes M. Schmitt - * - * @method bool isAuthenticated(TokenInterface $token = null) */ interface AuthenticationTrustResolverInterface { + /** + * Resolves whether the passed token implementation is authenticated. + */ + public function isAuthenticated(TokenInterface $token = null): bool; + /** * Resolves whether the passed token implementation is authenticated * using remember-me capabilities. From eda33055f47ea054028fd13852c50a3a09db3a5c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 12 Aug 2021 19:19:44 +0200 Subject: [PATCH 019/138] [Security] add return types --- Authentication/AuthenticationTrustResolver.php | 4 ++-- .../RememberMe/InMemoryTokenProvider.php | 2 +- .../RememberMe/PersistentTokenInterface.php | 16 ++++------------ Authentication/Token/AbstractToken.php | 11 ++++------- Authentication/Token/NullToken.php | 8 ++++---- Authentication/Token/RememberMeToken.php | 5 +---- Authentication/Token/Storage/TokenStorage.php | 2 +- .../Token/Storage/TokenStorageInterface.php | 4 +--- Authentication/Token/TokenInterface.php | 18 ++++-------------- AuthenticationEvents.php | 1 - Authorization/AccessDecisionManager.php | 2 +- .../AccessDecisionManagerInterface.php | 4 +--- Authorization/AuthorizationChecker.php | 1 - .../AuthorizationCheckerInterface.php | 4 +--- Authorization/ExpressionLanguageProvider.php | 2 +- Authorization/Voter/AuthenticatedVoter.php | 2 +- Authorization/Voter/ExpressionVoter.php | 2 +- Authorization/Voter/RoleVoter.php | 2 +- Authorization/Voter/Voter.php | 10 +++------- Authorization/Voter/VoterInterface.php | 2 +- Exception/AccessDeniedException.php | 10 ++-------- Exception/AccountExpiredException.php | 2 +- Exception/AccountStatusException.php | 4 +--- ...henticationCredentialsNotFoundException.php | 2 +- Exception/AuthenticationException.php | 9 ++------- Exception/AuthenticationExpiredException.php | 2 +- Exception/AuthenticationServiceException.php | 2 +- Exception/BadCredentialsException.php | 2 +- Exception/CookieTheftException.php | 2 +- Exception/CredentialsExpiredException.php | 2 +- ...CustomUserMessageAccountStatusException.php | 4 ++-- ...ustomUserMessageAuthenticationException.php | 4 ++-- Exception/DisabledException.php | 2 +- .../InsufficientAuthenticationException.php | 2 +- Exception/InvalidCsrfTokenException.php | 2 +- Exception/LockedException.php | 2 +- Exception/ProviderNotFoundException.php | 2 +- Exception/SessionUnavailableException.php | 2 +- Exception/TokenNotFoundException.php | 2 +- Exception/UserNotFoundException.php | 4 ++-- .../Token/SwitchUserTokenTest.php | 1 - .../Authorization/AuthorizationCheckerTest.php | 2 -- Tests/Exception/UserNotFoundExceptionTest.php | 1 - Tests/User/InMemoryUserProviderTest.php | 1 - Tests/User/InMemoryUserTest.php | 1 - User/ChainUserProvider.php | 11 ++++------- User/EquatableInterface.php | 4 +--- User/InMemoryUserChecker.php | 3 --- User/InMemoryUserProvider.php | 4 ++-- User/UserInterface.php | 2 +- Validator/Constraints/UserPassword.php | 2 +- 51 files changed, 67 insertions(+), 130 deletions(-) diff --git a/Authentication/AuthenticationTrustResolver.php b/Authentication/AuthenticationTrustResolver.php index 9abad2c9..52967c6a 100644 --- a/Authentication/AuthenticationTrustResolver.php +++ b/Authentication/AuthenticationTrustResolver.php @@ -29,7 +29,7 @@ public function isAuthenticated(TokenInterface $token = null): bool /** * {@inheritdoc} */ - public function isRememberMe(TokenInterface $token = null) + public function isRememberMe(TokenInterface $token = null): bool { return $token && $token instanceof RememberMeToken; } @@ -37,7 +37,7 @@ public function isRememberMe(TokenInterface $token = null) /** * {@inheritdoc} */ - public function isFullFledged(TokenInterface $token = null) + public function isFullFledged(TokenInterface $token = null): bool { return $this->isAuthenticated($token) && !$this->isRememberMe($token); } diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 571bbe02..8b0d932c 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -25,7 +25,7 @@ class InMemoryTokenProvider implements TokenProviderInterface /** * {@inheritdoc} */ - public function loadTokenBySeries(string $series) + public function loadTokenBySeries(string $series): PersistentTokenInterface { if (!isset($this->tokens[$series])) { throw new TokenNotFoundException('No token found.'); diff --git a/Authentication/RememberMe/PersistentTokenInterface.php b/Authentication/RememberMe/PersistentTokenInterface.php index 4ace802d..966e689e 100644 --- a/Authentication/RememberMe/PersistentTokenInterface.php +++ b/Authentication/RememberMe/PersistentTokenInterface.php @@ -21,31 +21,23 @@ interface PersistentTokenInterface { /** * Returns the class of the user. - * - * @return string */ - public function getClass(); + public function getClass(): string; /** * Returns the series. - * - * @return string */ - public function getSeries(); + public function getSeries(): string; /** * Returns the token value. - * - * @return string */ - public function getTokenValue(); + public function getTokenValue(): string; /** * Returns the time the token was last used. - * - * @return \DateTime */ - public function getLastUsed(); + public function getLastUsed(): \DateTime; /** * Returns the identifier used to authenticate (e.g. their email address or username). diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 6d0a62d5..42ede41c 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -11,9 +11,6 @@ namespace Symfony\Component\Security\Core\Authentication\Token; -use Symfony\Component\Security\Core\User\EquatableInterface; -use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; /** @@ -56,7 +53,7 @@ public function getUserIdentifier(): string /** * {@inheritdoc} */ - public function getUser() + public function getUser(): ?UserInterface { return $this->user; } @@ -123,7 +120,7 @@ public function __unserialize(array $data): void /** * {@inheritdoc} */ - public function getAttributes() + public function getAttributes(): array { return $this->attributes; } @@ -139,7 +136,7 @@ public function setAttributes(array $attributes) /** * {@inheritdoc} */ - public function hasAttribute(string $name) + public function hasAttribute(string $name): bool { return \array_key_exists($name, $this->attributes); } @@ -147,7 +144,7 @@ public function hasAttribute(string $name) /** * {@inheritdoc} */ - public function getAttribute(string $name) + public function getAttribute(string $name): mixed { if (!\array_key_exists($name, $this->attributes)) { throw new \InvalidArgumentException(sprintf('This token has no "%s" attribute.', $name)); diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index 5cd57b03..47e04eb4 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -28,7 +28,7 @@ public function getRoleNames(): array return []; } - public function getUser() + public function getUser(): ?UserInterface { return null; } @@ -47,7 +47,7 @@ public function eraseCredentials() { } - public function getAttributes() + public function getAttributes(): array { return []; } @@ -57,12 +57,12 @@ public function setAttributes(array $attributes) throw new \BadMethodCallException('Cannot set attributes of NullToken.'); } - public function hasAttribute(string $name) + public function hasAttribute(string $name): bool { return false; } - public function getAttribute(string $name) + public function getAttribute(string $name): mixed { return null; } diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index 2cdc7420..06d2d363 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -51,10 +51,7 @@ public function getFirewallName(): string return $this->firewallName; } - /** - * @return string - */ - public function getSecret() + public function getSecret(): string { return $this->secret; } diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 850c05e7..5026d82b 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -30,7 +30,7 @@ class TokenStorage implements TokenStorageInterface, ResetInterface /** * {@inheritdoc} */ - public function getToken() + public function getToken(): ?TokenInterface { if ($initializer = $this->initializer) { $this->initializer = null; diff --git a/Authentication/Token/Storage/TokenStorageInterface.php b/Authentication/Token/Storage/TokenStorageInterface.php index 1077a9bb..340f57d8 100644 --- a/Authentication/Token/Storage/TokenStorageInterface.php +++ b/Authentication/Token/Storage/TokenStorageInterface.php @@ -22,10 +22,8 @@ interface TokenStorageInterface { /** * Returns the current security token. - * - * @return TokenInterface|null */ - public function getToken(); + public function getToken(): ?TokenInterface; /** * Sets the authentication token. diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index a6a40d90..2554131c 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -40,11 +40,9 @@ public function getRoleNames(): array; /** * Returns a user representation. * - * @return UserInterface|null - * * @see AbstractToken::setUser() */ - public function getUser(); + public function getUser(): ?UserInterface; /** * Sets the authenticated user in the token. @@ -58,27 +56,19 @@ public function setUser(UserInterface $user); */ public function eraseCredentials(); - /** - * @return array - */ - public function getAttributes(); + public function getAttributes(): array; /** * @param array $attributes The token attributes */ public function setAttributes(array $attributes); - /** - * @return bool - */ - public function hasAttribute(string $name); + public function hasAttribute(string $name): bool; /** - * @return mixed - * * @throws \InvalidArgumentException When attribute doesn't exist for this token */ - public function getAttribute(string $name); + public function getAttribute(string $name): mixed; public function setAttribute(string $name, mixed $value); diff --git a/AuthenticationEvents.php b/AuthenticationEvents.php index 30498d0a..a1c3e5dd 100644 --- a/AuthenticationEvents.php +++ b/AuthenticationEvents.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Core; -use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent; use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; final class AuthenticationEvents diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index 94d8a35b..9a3a16bf 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -59,7 +59,7 @@ public function __construct(iterable $voters = [], string $strategy = self::STRA * * {@inheritdoc} */ - public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false) + public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false): bool { // Special case for AccessListener, do not remove the right side of the condition before 6.0 if (\count($attributes) > 1 && !$allowMultipleAttributes) { diff --git a/Authorization/AccessDecisionManagerInterface.php b/Authorization/AccessDecisionManagerInterface.php index 7c2f068e..f25c7e1b 100644 --- a/Authorization/AccessDecisionManagerInterface.php +++ b/Authorization/AccessDecisionManagerInterface.php @@ -25,8 +25,6 @@ interface AccessDecisionManagerInterface * * @param array $attributes An array of attributes associated with the method being invoked * @param mixed $object The object to secure - * - * @return bool */ - public function decide(TokenInterface $token, array $attributes, mixed $object = null); + public function decide(TokenInterface $token, array $attributes, mixed $object = null): bool; } diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index c50ed9e4..31b9a6dd 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Core\Authorization; -use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; diff --git a/Authorization/AuthorizationCheckerInterface.php b/Authorization/AuthorizationCheckerInterface.php index 97e53cb8..6f5a6022 100644 --- a/Authorization/AuthorizationCheckerInterface.php +++ b/Authorization/AuthorizationCheckerInterface.php @@ -22,8 +22,6 @@ interface AuthorizationCheckerInterface * Checks if the attribute is granted against the current authentication token and optionally supplied subject. * * @param mixed $attribute A single attribute to vote on (can be of any type, string and instance of Expression are supported by the core) - * - * @return bool */ - public function isGranted(mixed $attribute, mixed $subject = null); + public function isGranted(mixed $attribute, mixed $subject = null): bool; } diff --git a/Authorization/ExpressionLanguageProvider.php b/Authorization/ExpressionLanguageProvider.php index 74350039..755efd65 100644 --- a/Authorization/ExpressionLanguageProvider.php +++ b/Authorization/ExpressionLanguageProvider.php @@ -21,7 +21,7 @@ */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface { - public function getFunctions() + public function getFunctions(): array { return [ new ExpressionFunction('is_authenticated', function () { diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index cbbe924c..2c13018b 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -43,7 +43,7 @@ public function __construct(AuthenticationTrustResolverInterface $authentication /** * {@inheritdoc} */ - public function vote(TokenInterface $token, mixed $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes): int { if ($attributes === [self::PUBLIC_ACCESS]) { return VoterInterface::ACCESS_GRANTED; diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index 1db0f875..580ddc39 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -42,7 +42,7 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati /** * {@inheritdoc} */ - public function vote(TokenInterface $token, mixed $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes): int { $result = VoterInterface::ACCESS_ABSTAIN; $variables = null; diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index b8437a3b..4d8aecc6 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -30,7 +30,7 @@ public function __construct(string $prefix = 'ROLE_') /** * {@inheritdoc} */ - public function vote(TokenInterface $token, mixed $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes): int { $result = VoterInterface::ACCESS_ABSTAIN; $roles = $this->extractRoles($token); diff --git a/Authorization/Voter/Voter.php b/Authorization/Voter/Voter.php index d363ad4d..b465dd73 100644 --- a/Authorization/Voter/Voter.php +++ b/Authorization/Voter/Voter.php @@ -24,7 +24,7 @@ abstract class Voter implements VoterInterface /** * {@inheritdoc} */ - public function vote(TokenInterface $token, mixed $subject, array $attributes) + public function vote(TokenInterface $token, mixed $subject, array $attributes): int { // abstain vote by default in case none of the attributes are supported $vote = self::ACCESS_ABSTAIN; @@ -58,16 +58,12 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes) * Determines if the attribute and subject are supported by this voter. * * @param $subject The subject to secure, e.g. an object the user wants to access or any other PHP type - * - * @return bool */ - abstract protected function supports(string $attribute, mixed $subject); + abstract protected function supports(string $attribute, mixed $subject): bool; /** * Perform a single access check operation on a given attribute, subject and token. * It is safe to assume that $attribute and $subject already passed the "supports()" method check. - * - * @return bool */ - abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token); + abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool; } diff --git a/Authorization/Voter/VoterInterface.php b/Authorization/Voter/VoterInterface.php index 7e401c3f..6b446ff3 100644 --- a/Authorization/Voter/VoterInterface.php +++ b/Authorization/Voter/VoterInterface.php @@ -35,5 +35,5 @@ interface VoterInterface * * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED */ - public function vote(TokenInterface $token, mixed $subject, array $attributes); + public function vote(TokenInterface $token, mixed $subject, array $attributes): int; } diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index 2eb38e79..1ad76afc 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -26,10 +26,7 @@ public function __construct(string $message = 'Access Denied.', \Throwable $prev parent::__construct($message, 403, $previous); } - /** - * @return array - */ - public function getAttributes() + public function getAttributes(): array { return $this->attributes; } @@ -39,10 +36,7 @@ public function setAttributes(array|string $attributes) $this->attributes = (array) $attributes; } - /** - * @return mixed - */ - public function getSubject() + public function getSubject(): mixed { return $this->subject; } diff --git a/Exception/AccountExpiredException.php b/Exception/AccountExpiredException.php index 4a712637..b344d661 100644 --- a/Exception/AccountExpiredException.php +++ b/Exception/AccountExpiredException.php @@ -22,7 +22,7 @@ class AccountExpiredException extends AccountStatusException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Account has expired.'; } diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index 1b4e818a..919dce16 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -26,10 +26,8 @@ abstract class AccountStatusException extends AuthenticationException /** * Get the user. - * - * @return UserInterface */ - public function getUser() + public function getUser(): UserInterface { return $this->user; } diff --git a/Exception/AuthenticationCredentialsNotFoundException.php b/Exception/AuthenticationCredentialsNotFoundException.php index 8595bed8..55e44424 100644 --- a/Exception/AuthenticationCredentialsNotFoundException.php +++ b/Exception/AuthenticationCredentialsNotFoundException.php @@ -23,7 +23,7 @@ class AuthenticationCredentialsNotFoundException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Authentication credentials could not be found.'; } diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index d971d11c..606c812f 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -23,10 +23,7 @@ class AuthenticationException extends RuntimeException { private $token; - /** - * @return TokenInterface|null - */ - public function getToken() + public function getToken(): ?TokenInterface { return $this->token; } @@ -89,10 +86,8 @@ public function getMessageKey() /** * Message data to be used by the translation component. - * - * @return array */ - public function getMessageData() + public function getMessageData(): array { return []; } diff --git a/Exception/AuthenticationExpiredException.php b/Exception/AuthenticationExpiredException.php index e3fce37b..a1a26fb1 100644 --- a/Exception/AuthenticationExpiredException.php +++ b/Exception/AuthenticationExpiredException.php @@ -24,7 +24,7 @@ class AuthenticationExpiredException extends AccountStatusException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Authentication expired because your account information has changed.'; } diff --git a/Exception/AuthenticationServiceException.php b/Exception/AuthenticationServiceException.php index 66f051d0..c57a969f 100644 --- a/Exception/AuthenticationServiceException.php +++ b/Exception/AuthenticationServiceException.php @@ -22,7 +22,7 @@ class AuthenticationServiceException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Authentication request could not be processed due to a system problem.'; } diff --git a/Exception/BadCredentialsException.php b/Exception/BadCredentialsException.php index be061c7b..e89598eb 100644 --- a/Exception/BadCredentialsException.php +++ b/Exception/BadCredentialsException.php @@ -22,7 +22,7 @@ class BadCredentialsException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Invalid credentials.'; } diff --git a/Exception/CookieTheftException.php b/Exception/CookieTheftException.php index af971684..4b8ebcf1 100644 --- a/Exception/CookieTheftException.php +++ b/Exception/CookieTheftException.php @@ -23,7 +23,7 @@ class CookieTheftException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Cookie has already been used by someone else.'; } diff --git a/Exception/CredentialsExpiredException.php b/Exception/CredentialsExpiredException.php index bcc1267a..f21fcf8d 100644 --- a/Exception/CredentialsExpiredException.php +++ b/Exception/CredentialsExpiredException.php @@ -22,7 +22,7 @@ class CredentialsExpiredException extends AccountStatusException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Credentials have expired.'; } diff --git a/Exception/CustomUserMessageAccountStatusException.php b/Exception/CustomUserMessageAccountStatusException.php index 3594b9bd..8bf27f5e 100644 --- a/Exception/CustomUserMessageAccountStatusException.php +++ b/Exception/CustomUserMessageAccountStatusException.php @@ -46,12 +46,12 @@ public function setSafeMessage(string $messageKey, array $messageData = []) $this->messageData = $messageData; } - public function getMessageKey() + public function getMessageKey(): string { return $this->messageKey; } - public function getMessageData() + public function getMessageData(): array { return $this->messageData; } diff --git a/Exception/CustomUserMessageAuthenticationException.php b/Exception/CustomUserMessageAuthenticationException.php index 799d7e0c..97e45f00 100644 --- a/Exception/CustomUserMessageAuthenticationException.php +++ b/Exception/CustomUserMessageAuthenticationException.php @@ -45,12 +45,12 @@ public function setSafeMessage(string $messageKey, array $messageData = []) $this->messageData = $messageData; } - public function getMessageKey() + public function getMessageKey(): string { return $this->messageKey; } - public function getMessageData() + public function getMessageData(): array { return $this->messageData; } diff --git a/Exception/DisabledException.php b/Exception/DisabledException.php index e9b784fb..f598a16b 100644 --- a/Exception/DisabledException.php +++ b/Exception/DisabledException.php @@ -22,7 +22,7 @@ class DisabledException extends AccountStatusException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Account is disabled.'; } diff --git a/Exception/InsufficientAuthenticationException.php b/Exception/InsufficientAuthenticationException.php index e33ef6ac..9d26a304 100644 --- a/Exception/InsufficientAuthenticationException.php +++ b/Exception/InsufficientAuthenticationException.php @@ -24,7 +24,7 @@ class InsufficientAuthenticationException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Not privileged to request the resource.'; } diff --git a/Exception/InvalidCsrfTokenException.php b/Exception/InvalidCsrfTokenException.php index 84be8556..7a90f0ef 100644 --- a/Exception/InvalidCsrfTokenException.php +++ b/Exception/InvalidCsrfTokenException.php @@ -22,7 +22,7 @@ class InvalidCsrfTokenException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Invalid CSRF token.'; } diff --git a/Exception/LockedException.php b/Exception/LockedException.php index fffae74d..8943aee7 100644 --- a/Exception/LockedException.php +++ b/Exception/LockedException.php @@ -22,7 +22,7 @@ class LockedException extends AccountStatusException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Account is locked.'; } diff --git a/Exception/ProviderNotFoundException.php b/Exception/ProviderNotFoundException.php index af2e1b57..584a592e 100644 --- a/Exception/ProviderNotFoundException.php +++ b/Exception/ProviderNotFoundException.php @@ -23,7 +23,7 @@ class ProviderNotFoundException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'No authentication provider found to support the authentication token.'; } diff --git a/Exception/SessionUnavailableException.php b/Exception/SessionUnavailableException.php index 90b858a7..1d55cb1e 100644 --- a/Exception/SessionUnavailableException.php +++ b/Exception/SessionUnavailableException.php @@ -28,7 +28,7 @@ class SessionUnavailableException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'No session available, it either timed out or cookies are not enabled.'; } diff --git a/Exception/TokenNotFoundException.php b/Exception/TokenNotFoundException.php index b050302a..290b4f56 100644 --- a/Exception/TokenNotFoundException.php +++ b/Exception/TokenNotFoundException.php @@ -22,7 +22,7 @@ class TokenNotFoundException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'No token could be found.'; } diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 855f5f31..5da4f0e4 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -24,7 +24,7 @@ class UserNotFoundException extends AuthenticationException /** * {@inheritdoc} */ - public function getMessageKey() + public function getMessageKey(): string { return 'Username could not be found.'; } @@ -48,7 +48,7 @@ public function setUserIdentifier(string $identifier): void /** * {@inheritdoc} */ - public function getMessageData() + public function getMessageData(): array { return ['{{ username }}' => $this->identifier, '{{ user_identifier }}' => $this->identifier]; } diff --git a/Tests/Authentication/Token/SwitchUserTokenTest.php b/Tests/Authentication/Token/SwitchUserTokenTest.php index a6d29a89..86551397 100644 --- a/Tests/Authentication/Token/SwitchUserTokenTest.php +++ b/Tests/Authentication/Token/SwitchUserTokenTest.php @@ -16,7 +16,6 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Tests\Authentication\Token\Fixtures\CustomUser; use Symfony\Component\Security\Core\User\InMemoryUser; -use Symfony\Component\Security\Core\User\UserInterface; class SwitchUserTokenTest extends TestCase { diff --git a/Tests/Authorization/AuthorizationCheckerTest.php b/Tests/Authorization/AuthorizationCheckerTest.php index a3dd46a1..957d9938 100644 --- a/Tests/Authorization/AuthorizationCheckerTest.php +++ b/Tests/Authorization/AuthorizationCheckerTest.php @@ -12,13 +12,11 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; -use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; class AuthorizationCheckerTest extends TestCase diff --git a/Tests/Exception/UserNotFoundExceptionTest.php b/Tests/Exception/UserNotFoundExceptionTest.php index f526b37f..d0c812eb 100644 --- a/Tests/Exception/UserNotFoundExceptionTest.php +++ b/Tests/Exception/UserNotFoundExceptionTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Core\Tests\Exception; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; class UserNotFoundExceptionTest extends TestCase diff --git a/Tests/User/InMemoryUserProviderTest.php b/Tests/User/InMemoryUserProviderTest.php index aa058bba..a2525962 100644 --- a/Tests/User/InMemoryUserProviderTest.php +++ b/Tests/User/InMemoryUserProviderTest.php @@ -16,7 +16,6 @@ use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; -use Symfony\Component\Security\Core\User\User; class InMemoryUserProviderTest extends TestCase { diff --git a/Tests/User/InMemoryUserTest.php b/Tests/User/InMemoryUserTest.php index fb803747..8c0b1b38 100644 --- a/Tests/User/InMemoryUserTest.php +++ b/Tests/User/InMemoryUserTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; -use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 6a6f2833..e0e8c6e7 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -34,10 +34,7 @@ public function __construct(iterable $providers) $this->providers = $providers; } - /** - * @return array - */ - public function getProviders() + public function getProviders(): array { if ($this->providers instanceof \Traversable) { return iterator_to_array($this->providers); @@ -49,7 +46,7 @@ public function getProviders() /** * @internal for compatibility with Symfony 5.4 */ - public function loadUserByUsername(string $username) + public function loadUserByUsername(string $username): UserInterface { return $this->loadUserByIdentifier($username); } @@ -72,7 +69,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface /** * {@inheritdoc} */ - public function refreshUser(UserInterface $user) + public function refreshUser(UserInterface $user): UserInterface { $supportedUserFound = false; @@ -104,7 +101,7 @@ public function refreshUser(UserInterface $user) /** * {@inheritdoc} */ - public function supportsClass(string $class) + public function supportsClass(string $class): bool { foreach ($this->providers as $provider) { if ($provider->supportsClass($class)) { diff --git a/User/EquatableInterface.php b/User/EquatableInterface.php index 70408101..3fa9e488 100644 --- a/User/EquatableInterface.php +++ b/User/EquatableInterface.php @@ -25,8 +25,6 @@ interface EquatableInterface * * However, you do not need to compare every attribute, but only those that * are relevant for assessing whether re-authentication is required. - * - * @return bool */ - public function isEqualTo(UserInterface $user); + public function isEqualTo(UserInterface $user): bool; } diff --git a/User/InMemoryUserChecker.php b/User/InMemoryUserChecker.php index 5649cc99..f738ee00 100644 --- a/User/InMemoryUserChecker.php +++ b/User/InMemoryUserChecker.php @@ -11,10 +11,7 @@ namespace Symfony\Component\Security\Core\User; -use Symfony\Component\Security\Core\Exception\AccountExpiredException; -use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; use Symfony\Component\Security\Core\Exception\DisabledException; -use Symfony\Component\Security\Core\Exception\LockedException; /** * Checks the state of the in-memory user account. diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index a472a6b1..bd813d55 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -69,7 +69,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface /** * {@inheritdoc} */ - public function refreshUser(UserInterface $user) + public function refreshUser(UserInterface $user): UserInterface { if (!$user instanceof InMemoryUser) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); @@ -84,7 +84,7 @@ public function refreshUser(UserInterface $user) /** * {@inheritdoc} */ - public function supportsClass(string $class) + public function supportsClass(string $class): bool { return InMemoryUser::class == $class; } diff --git a/User/UserInterface.php b/User/UserInterface.php index 50da2f53..d7db2dbc 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -44,7 +44,7 @@ interface UserInterface * * @return string[] */ - public function getRoles(); + public function getRoles(): array; /** * Removes sensitive data from the user. diff --git a/Validator/Constraints/UserPassword.php b/Validator/Constraints/UserPassword.php index 7094ca98..ba37598e 100644 --- a/Validator/Constraints/UserPassword.php +++ b/Validator/Constraints/UserPassword.php @@ -34,7 +34,7 @@ public function __construct(array $options = null, string $message = null, strin /** * {@inheritdoc} */ - public function validatedBy() + public function validatedBy(): string { return $this->service; } From cf194c61b57e260a0c5e7ce41f2a11be78f72361 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Aug 2021 12:09:15 +0200 Subject: [PATCH 020/138] Add missing return types --- Authentication/AuthenticationTrustResolverInterface.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Authentication/AuthenticationTrustResolverInterface.php b/Authentication/AuthenticationTrustResolverInterface.php index 39c20547..90f2ec2f 100644 --- a/Authentication/AuthenticationTrustResolverInterface.php +++ b/Authentication/AuthenticationTrustResolverInterface.php @@ -28,15 +28,11 @@ public function isAuthenticated(TokenInterface $token = null): bool; /** * Resolves whether the passed token implementation is authenticated * using remember-me capabilities. - * - * @return bool */ - public function isRememberMe(TokenInterface $token = null); + public function isRememberMe(TokenInterface $token = null): bool; /** * Resolves whether the passed token implementation is fully authenticated. - * - * @return bool */ - public function isFullFledged(TokenInterface $token = null); + public function isFullFledged(TokenInterface $token = null): bool; } From 3d475835c9a40f30d5d7a5613fe2de878d030897 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 6 Sep 2021 19:10:44 +0200 Subject: [PATCH 021/138] [Security] Relax return type on VoterInterface --- Authorization/Voter/VoterInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authorization/Voter/VoterInterface.php b/Authorization/Voter/VoterInterface.php index 6b446ff3..7e401c3f 100644 --- a/Authorization/Voter/VoterInterface.php +++ b/Authorization/Voter/VoterInterface.php @@ -35,5 +35,5 @@ interface VoterInterface * * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED */ - public function vote(TokenInterface $token, mixed $subject, array $attributes): int; + public function vote(TokenInterface $token, mixed $subject, array $attributes); } From 7921c901b60729d78329e453aaf3dad4c01cf3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 20 May 2021 10:12:11 +0200 Subject: [PATCH 022/138] Remove deprecate session service --- .../Storage/UsageTrackingTokenStorage.php | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index 4b2cac74..d9f7f12a 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -79,33 +79,11 @@ public static function getSubscribedServices(): array private function getSession(): SessionInterface { - // BC for symfony/security-bundle < 5.3 - if ($this->container->has('session')) { - trigger_deprecation('symfony/security-core', '5.3', 'Injecting the "session" in "%s" is deprecated, inject the "request_stack" instead.', __CLASS__); - - return $this->container->get('session'); - } - return $this->container->get('request_stack')->getSession(); } private function shouldTrackUsage(): bool { - if (!$this->enableUsageTracking) { - return false; - } - - // BC for symfony/security-bundle < 5.3 - if ($this->container->has('session')) { - return true; - } - - if (!$this->container->get('request_stack')->getMainRequest()) { - trigger_deprecation('symfony/security-core', '5.3', 'Using "%s" (service ID: "security.token_storage") outside the request-response cycle is deprecated, use the "%s" class (service ID: "security.untracked_token_storage") instead or disable usage tracking using "disableUsageTracking()".', __CLASS__, TokenStorage::class); - - return false; - } - - return true; + return $this->enableUsageTracking; } } From 446a162b41e5646ab0cd02bb04a292b994faf327 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 7 Sep 2021 14:27:46 +0200 Subject: [PATCH 023/138] Turn `@method` annotations into real method declarations --- Authentication/Token/TokenInterface.php | 7 +++++-- User/UserProviderInterface.php | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index 2554131c..d9e1bb4c 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -16,8 +16,6 @@ /** * TokenInterface is the interface for the user authentication information. * - * @method string getUserIdentifier() returns the user identifier used during authentication (e.g. a user's email address or username) - * * @author Fabien Potencier * @author Johannes M. Schmitt */ @@ -30,6 +28,11 @@ interface TokenInterface */ public function __toString(): string; + /** + * Returns the user identifier used during authentication (e.g. a user's email address or username). + */ + public function getUserIdentifier(): string; + /** * Returns the user roles. * diff --git a/User/UserProviderInterface.php b/User/UserProviderInterface.php index 33d489f6..ec90d413 100644 --- a/User/UserProviderInterface.php +++ b/User/UserProviderInterface.php @@ -27,10 +27,6 @@ * configuration, web service). This is totally independent of how the authentication * information is submitted or what the UserInterface object looks like. * - * @see UserInterface - * - * @method UserInterface loadUserByIdentifier(string $identifier) - * * @author Fabien Potencier */ interface UserProviderInterface From c6a886e485b4ec42d3d8a1f4ca35b80dfeecc6ac Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 7 Sep 2021 15:39:06 +0200 Subject: [PATCH 024/138] Remove deprecated code paths --- Tests/Authentication/Token/AbstractTokenTest.php | 3 --- Tests/Authentication/Token/SwitchUserTokenTest.php | 2 -- Tests/Authorization/AccessDecisionManagerTest.php | 3 --- Tests/Authorization/Voter/RoleVoterTest.php | 3 --- Tests/User/InMemoryUserProviderTest.php | 3 --- Tests/User/InMemoryUserTest.php | 3 --- composer.json | 1 - 7 files changed, 18 deletions(-) diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index b8e41d45..03cc6d17 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -12,15 +12,12 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; class AbstractTokenTest extends TestCase { - use ExpectDeprecationTrait; - /** * @dataProvider provideUsers */ diff --git a/Tests/Authentication/Token/SwitchUserTokenTest.php b/Tests/Authentication/Token/SwitchUserTokenTest.php index 86551397..2fc6e321 100644 --- a/Tests/Authentication/Token/SwitchUserTokenTest.php +++ b/Tests/Authentication/Token/SwitchUserTokenTest.php @@ -68,8 +68,6 @@ public function testSerializeNullImpersonateUrl() * ) * ) * ) - * - * @group legacy */ public function testUnserializeOldToken() { diff --git a/Tests/Authorization/AccessDecisionManagerTest.php b/Tests/Authorization/AccessDecisionManagerTest.php index b86811bb..5f2e5d65 100644 --- a/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Tests/Authorization/AccessDecisionManagerTest.php @@ -12,15 +12,12 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class AccessDecisionManagerTest extends TestCase { - use ExpectDeprecationTrait; - public function testSetUnsupportedStrategy() { $this->expectException(\InvalidArgumentException::class); diff --git a/Tests/Authorization/Voter/RoleVoterTest.php b/Tests/Authorization/Voter/RoleVoterTest.php index c0a13895..6331e847 100644 --- a/Tests/Authorization/Voter/RoleVoterTest.php +++ b/Tests/Authorization/Voter/RoleVoterTest.php @@ -12,15 +12,12 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class RoleVoterTest extends TestCase { - use ExpectDeprecationTrait; - /** * @dataProvider getVoteTests */ diff --git a/Tests/User/InMemoryUserProviderTest.php b/Tests/User/InMemoryUserProviderTest.php index a2525962..da0d832d 100644 --- a/Tests/User/InMemoryUserProviderTest.php +++ b/Tests/User/InMemoryUserProviderTest.php @@ -12,15 +12,12 @@ namespace Symfony\Component\Security\Core\Tests\User; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; class InMemoryUserProviderTest extends TestCase { - use ExpectDeprecationTrait; - public function testConstructor() { $provider = $this->createProvider(); diff --git a/Tests/User/InMemoryUserTest.php b/Tests/User/InMemoryUserTest.php index 8c0b1b38..0e64bce5 100644 --- a/Tests/User/InMemoryUserTest.php +++ b/Tests/User/InMemoryUserTest.php @@ -12,14 +12,11 @@ namespace Symfony\Component\Security\Core\Tests\User; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; class InMemoryUserTest extends TestCase { - use ExpectDeprecationTrait; - public function testConstructorException() { $this->expectException(\InvalidArgumentException::class); diff --git a/composer.json b/composer.json index 145479e0..de0dbd21 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,6 @@ "php": ">=8.0.2", "symfony/event-dispatcher-contracts": "^1.1|^2.0|^3.0", "symfony/service-contracts": "^1.1.6|^2.0|^3.0", - "symfony/deprecation-contracts": "^2.1|^3.0", "symfony/password-hasher": "^5.4|^6.0" }, "require-dev": { From 38c150c7ab4be5bd1bd992a62c6c9a368be3039b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 29 Oct 2021 09:41:55 +0200 Subject: [PATCH 025/138] fix merge --- Tests/Authorization/Voter/AuthenticatedVoterTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Authorization/Voter/AuthenticatedVoterTest.php index 8ce1e827..308c0999 100644 --- a/Tests/Authorization/Voter/AuthenticatedVoterTest.php +++ b/Tests/Authorization/Voter/AuthenticatedVoterTest.php @@ -67,8 +67,6 @@ public function provideAttributes() { yield [AuthenticatedVoter::IS_AUTHENTICATED_FULLY, true]; yield [AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED, true]; - yield [AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY, true]; - yield [AuthenticatedVoter::IS_ANONYMOUS, true]; yield [AuthenticatedVoter::IS_AUTHENTICATED, true]; yield [AuthenticatedVoter::IS_IMPERSONATOR, true]; yield [AuthenticatedVoter::IS_REMEMBERED, true]; From 416768446d98eb57ee61311b4f7b151b948f09fd Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Fri, 29 Oct 2021 14:59:55 +0200 Subject: [PATCH 026/138] [Security] Remove deprecated access decision code Signed-off-by: Alexander M. Turek --- Authorization/AccessDecisionManager.php | 75 ++-------------- .../Strategy/AffirmativeStrategy.php | 5 +- Authorization/Strategy/ConsensusStrategy.php | 4 +- Authorization/Strategy/PriorityStrategy.php | 2 +- Authorization/Strategy/UnanimousStrategy.php | 2 +- CHANGELOG.md | 1 + .../AccessDecisionManagerTest.php | 87 ------------------- .../TraceableAccessDecisionManagerTest.php | 7 +- 8 files changed, 16 insertions(+), 167 deletions(-) diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index 9fc8dee9..eccbec9b 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -14,9 +14,6 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface; use Symfony\Component\Security\Core\Authorization\Strategy\AffirmativeStrategy; -use Symfony\Component\Security\Core\Authorization\Strategy\ConsensusStrategy; -use Symfony\Component\Security\Core\Authorization\Strategy\PriorityStrategy; -use Symfony\Component\Security\Core\Authorization\Strategy\UnanimousStrategy; use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Exception\InvalidArgumentException; @@ -26,61 +23,26 @@ * that use decision voters. * * @author Fabien Potencier - * - * @final since Symfony 5.4 */ -class AccessDecisionManager implements AccessDecisionManagerInterface +final class AccessDecisionManager implements AccessDecisionManagerInterface { - /** - * @deprecated use {@see AffirmativeStrategy} instead - */ - public const STRATEGY_AFFIRMATIVE = 'affirmative'; - - /** - * @deprecated use {@see ConsensusStrategy} instead - */ - public const STRATEGY_CONSENSUS = 'consensus'; - - /** - * @deprecated use {@see UnanimousStrategy} instead - */ - public const STRATEGY_UNANIMOUS = 'unanimous'; - - /** - * @deprecated use {@see PriorityStrategy} instead - */ - public const STRATEGY_PRIORITY = 'priority'; - private const VALID_VOTES = [ VoterInterface::ACCESS_GRANTED => true, VoterInterface::ACCESS_DENIED => true, VoterInterface::ACCESS_ABSTAIN => true, ]; - private $voters; - private $votersCacheAttributes; - private $votersCacheObject; - private $strategy; + private iterable $voters; + private array $votersCacheAttributes = []; + private array $votersCacheObject = []; + private AccessDecisionStrategyInterface $strategy; /** - * @param iterable $voters An array or an iterator of VoterInterface instances - * @param AccessDecisionStrategyInterface|null $strategy The vote strategy - * - * @throws \InvalidArgumentException + * @param iterable $voters An array or an iterator of VoterInterface instances */ - public function __construct(iterable $voters = [], /* AccessDecisionStrategyInterface */ $strategy = null) + public function __construct(iterable $voters = [], AccessDecisionStrategyInterface $strategy = null) { $this->voters = $voters; - if (\is_string($strategy)) { - trigger_deprecation('symfony/security-core', '5.4', 'Passing the access decision strategy as a string is deprecated, pass an instance of "%s" instead.', AccessDecisionStrategyInterface::class); - $allowIfAllAbstainDecisions = 3 <= \func_num_args() && func_get_arg(2); - $allowIfEqualGrantedDeniedDecisions = 4 > \func_num_args() || func_get_arg(3); - - $strategy = $this->createStrategy($strategy, $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions); - } elseif (null !== $strategy && !$strategy instanceof AccessDecisionStrategyInterface) { - throw new \TypeError(sprintf('"%s": Parameter #2 ($strategy) is expected to be an instance of "%s" or null, "%s" given.', __METHOD__, AccessDecisionStrategyInterface::class, get_debug_type($strategy))); - } - $this->strategy = $strategy ?? new AffirmativeStrategy(); } @@ -102,11 +64,9 @@ public function decide(TokenInterface $token, array $attributes, mixed $object = } /** - * @param mixed $object - * * @return \Traversable */ - private function collectResults(TokenInterface $token, array $attributes, $object): \Traversable + private function collectResults(TokenInterface $token, array $attributes, mixed $object): \Traversable { foreach ($this->getVoters($attributes, $object) as $voter) { $result = $voter->vote($token, $object, $attributes); @@ -118,25 +78,6 @@ private function collectResults(TokenInterface $token, array $attributes, $objec } } - /** - * @throws \InvalidArgumentException if the $strategy is invalid - */ - private function createStrategy(string $strategy, bool $allowIfAllAbstainDecisions, bool $allowIfEqualGrantedDeniedDecisions): AccessDecisionStrategyInterface - { - switch ($strategy) { - case self::STRATEGY_AFFIRMATIVE: - return new AffirmativeStrategy($allowIfAllAbstainDecisions); - case self::STRATEGY_CONSENSUS: - return new ConsensusStrategy($allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions); - case self::STRATEGY_UNANIMOUS: - return new UnanimousStrategy($allowIfAllAbstainDecisions); - case self::STRATEGY_PRIORITY: - return new PriorityStrategy($allowIfAllAbstainDecisions); - } - - throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy)); - } - /** * @return iterable */ diff --git a/Authorization/Strategy/AffirmativeStrategy.php b/Authorization/Strategy/AffirmativeStrategy.php index 09011999..3d6a5df3 100644 --- a/Authorization/Strategy/AffirmativeStrategy.php +++ b/Authorization/Strategy/AffirmativeStrategy.php @@ -24,10 +24,7 @@ */ final class AffirmativeStrategy implements AccessDecisionStrategyInterface, \Stringable { - /** - * @var bool - */ - private $allowIfAllAbstainDecisions; + private bool $allowIfAllAbstainDecisions; public function __construct(bool $allowIfAllAbstainDecisions = false) { diff --git a/Authorization/Strategy/ConsensusStrategy.php b/Authorization/Strategy/ConsensusStrategy.php index 0f617801..489b3428 100644 --- a/Authorization/Strategy/ConsensusStrategy.php +++ b/Authorization/Strategy/ConsensusStrategy.php @@ -32,8 +32,8 @@ */ final class ConsensusStrategy implements AccessDecisionStrategyInterface, \Stringable { - private $allowIfAllAbstainDecisions; - private $allowIfEqualGrantedDeniedDecisions; + private bool $allowIfAllAbstainDecisions; + private bool $allowIfEqualGrantedDeniedDecisions; public function __construct(bool $allowIfAllAbstainDecisions = false, bool $allowIfEqualGrantedDeniedDecisions = true) { diff --git a/Authorization/Strategy/PriorityStrategy.php b/Authorization/Strategy/PriorityStrategy.php index b2b416d7..d1942ede 100644 --- a/Authorization/Strategy/PriorityStrategy.php +++ b/Authorization/Strategy/PriorityStrategy.php @@ -25,7 +25,7 @@ */ final class PriorityStrategy implements AccessDecisionStrategyInterface, \Stringable { - private $allowIfAllAbstainDecisions; + private bool $allowIfAllAbstainDecisions; public function __construct(bool $allowIfAllAbstainDecisions = false) { diff --git a/Authorization/Strategy/UnanimousStrategy.php b/Authorization/Strategy/UnanimousStrategy.php index b2289f96..092fe3a0 100644 --- a/Authorization/Strategy/UnanimousStrategy.php +++ b/Authorization/Strategy/UnanimousStrategy.php @@ -24,7 +24,7 @@ */ final class UnanimousStrategy implements AccessDecisionStrategyInterface, \Stringable { - private $allowIfAllAbstainDecisions; + private bool $allowIfAllAbstainDecisions; public function __construct(bool $allowIfAllAbstainDecisions = false) { diff --git a/CHANGELOG.md b/CHANGELOG.md index dff9472f..c30f0fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Remove all classes in the `Core\Encoder\` sub-namespace, use the `PasswordHasher` component instead * Remove methods `getPassword()` and `getSalt()` from `UserInterface`, use `PasswordAuthenticatedUserInterface` or `LegacyPasswordAuthenticatedUserInterface` instead +* `AccessDecisionManager` requires the strategy to be passed as in instance of `AccessDecisionStrategyInterface` 5.4 --- diff --git a/Tests/Authorization/AccessDecisionManagerTest.php b/Tests/Authorization/AccessDecisionManagerTest.php index 35e7b57c..4712ae77 100644 --- a/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Tests/Authorization/AccessDecisionManagerTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface; @@ -22,32 +21,6 @@ class AccessDecisionManagerTest extends TestCase { - use ExpectDeprecationTrait; - - /** - * @group legacy - */ - public function testSetUnsupportedStrategy() - { - $this->expectException(\InvalidArgumentException::class); - new AccessDecisionManager([$this->getVoter(VoterInterface::ACCESS_GRANTED)], 'fooBar'); - } - - /** - * @group legacy - * - * @dataProvider getStrategyTests - */ - public function testStrategies($strategy, $voters, $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions, $expected) - { - $token = $this->createMock(TokenInterface::class); - - $this->expectDeprecation('Since symfony/security-core 5.4: Passing the access decision strategy as a string is deprecated, pass an instance of "Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface" instead.'); - $manager = new AccessDecisionManager($voters, $strategy, $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions); - - $this->assertSame($expected, $manager->decide($token, ['ROLE_FOO'])); - } - public function provideBadVoterResults(): array { return [ @@ -92,66 +65,6 @@ public function decide(\Traversable $results): bool $this->assertTrue($manager->decide($token, ['ROLE_FOO'])); } - public function getStrategyTests() - { - return [ - // affirmative - [AccessDecisionManager::STRATEGY_AFFIRMATIVE, $this->getVoters(1, 0, 0), false, true, true], - [AccessDecisionManager::STRATEGY_AFFIRMATIVE, $this->getVoters(1, 2, 0), false, true, true], - [AccessDecisionManager::STRATEGY_AFFIRMATIVE, $this->getVoters(0, 1, 0), false, true, false], - [AccessDecisionManager::STRATEGY_AFFIRMATIVE, $this->getVoters(0, 0, 1), false, true, false], - [AccessDecisionManager::STRATEGY_AFFIRMATIVE, $this->getVoters(0, 0, 1), true, true, true], - - // consensus - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(1, 0, 0), false, true, true], - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(1, 2, 0), false, true, false], - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(2, 1, 0), false, true, true], - - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(0, 0, 1), false, true, false], - - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(0, 0, 1), true, true, true], - - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(2, 2, 0), false, true, true], - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(2, 2, 1), false, true, true], - - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(2, 2, 0), false, false, false], - [AccessDecisionManager::STRATEGY_CONSENSUS, $this->getVoters(2, 2, 1), false, false, false], - - // unanimous - [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(1, 0, 0), false, true, true], - [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(1, 0, 1), false, true, true], - [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(1, 1, 0), false, true, false], - - [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(0, 0, 2), false, true, false], - [AccessDecisionManager::STRATEGY_UNANIMOUS, $this->getVoters(0, 0, 2), true, true, true], - - // priority - [AccessDecisionManager::STRATEGY_PRIORITY, [ - $this->getVoter(VoterInterface::ACCESS_ABSTAIN), - $this->getVoter(VoterInterface::ACCESS_GRANTED), - $this->getVoter(VoterInterface::ACCESS_DENIED), - $this->getVoter(VoterInterface::ACCESS_DENIED), - ], true, true, true], - - [AccessDecisionManager::STRATEGY_PRIORITY, [ - $this->getVoter(VoterInterface::ACCESS_ABSTAIN), - $this->getVoter(VoterInterface::ACCESS_DENIED), - $this->getVoter(VoterInterface::ACCESS_GRANTED), - $this->getVoter(VoterInterface::ACCESS_GRANTED), - ], true, true, false], - - [AccessDecisionManager::STRATEGY_PRIORITY, [ - $this->getVoter(VoterInterface::ACCESS_ABSTAIN), - $this->getVoter(VoterInterface::ACCESS_ABSTAIN), - ], false, true, false], - - [AccessDecisionManager::STRATEGY_PRIORITY, [ - $this->getVoter(VoterInterface::ACCESS_ABSTAIN), - $this->getVoter(VoterInterface::ACCESS_ABSTAIN), - ], true, true, true], - ]; - } - public function testCacheableVoters() { $token = $this->createMock(TokenInterface::class); diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index d4f89396..4b97885e 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; +use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; @@ -26,11 +27,7 @@ class TraceableAccessDecisionManagerTest extends TestCase public function testDecideLog(array $expectedLog, array $attributes, $object, array $voterVotes, bool $result) { $token = $this->createMock(TokenInterface::class); - - $admMock = $this - ->getMockBuilder(AccessDecisionManager::class) - ->setMethods(['decide']) - ->getMock(); + $admMock = $this->createMock(AccessDecisionManagerInterface::class); $adm = new TraceableAccessDecisionManager($admMock); From f721c776b64faf6e8501ae352276fccc5d56826c Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 1 Nov 2021 18:47:36 +0100 Subject: [PATCH 027/138] [Security] Add types to private properties Signed-off-by: Alexander M. Turek --- Authentication/RememberMe/CacheTokenVerifier.php | 6 +++--- Authentication/RememberMe/InMemoryTokenProvider.php | 2 +- Authentication/RememberMe/PersistentToken.php | 10 +++++----- Authentication/Token/AbstractToken.php | 6 +++--- Authentication/Token/PreAuthenticatedToken.php | 2 +- Authentication/Token/RememberMeToken.php | 4 ++-- Authentication/Token/Storage/TokenStorage.php | 6 +++--- .../Token/Storage/UsageTrackingTokenStorage.php | 6 +++--- Authentication/Token/SwitchUserToken.php | 4 ++-- Authentication/Token/UsernamePasswordToken.php | 2 +- Authorization/AuthorizationChecker.php | 6 ++---- Authorization/TraceableAccessDecisionManager.php | 11 ++++++----- Authorization/Voter/AuthenticatedVoter.php | 2 +- Authorization/Voter/ExpressionVoter.php | 8 ++++---- Authorization/Voter/RoleHierarchyVoter.php | 2 +- Authorization/Voter/RoleVoter.php | 2 +- Authorization/Voter/TraceableVoter.php | 4 ++-- Event/AuthenticationEvent.php | 2 +- Event/VoteEvent.php | 8 ++++---- Exception/AccessDeniedException.php | 4 ++-- Exception/AccountStatusException.php | 2 +- Exception/AuthenticationException.php | 2 +- Exception/CustomUserMessageAccountStatusException.php | 5 ++--- .../CustomUserMessageAuthenticationException.php | 5 ++--- Exception/LazyResponseException.php | 2 +- .../TooManyLoginAttemptsAuthenticationException.php | 2 +- Exception/UserNotFoundException.php | 2 +- Role/RoleHierarchy.php | 5 +---- Security.php | 2 +- Signature/ExpiredSignatureStorage.php | 4 ++-- Signature/SignatureHasher.php | 10 +++++----- User/ChainUserProvider.php | 4 ++-- User/InMemoryUser.php | 8 ++++---- User/InMemoryUserProvider.php | 5 ++++- Validator/Constraints/UserPasswordValidator.php | 4 ++-- 35 files changed, 78 insertions(+), 81 deletions(-) diff --git a/Authentication/RememberMe/CacheTokenVerifier.php b/Authentication/RememberMe/CacheTokenVerifier.php index dabc7190..55cbdcf4 100644 --- a/Authentication/RememberMe/CacheTokenVerifier.php +++ b/Authentication/RememberMe/CacheTokenVerifier.php @@ -18,9 +18,9 @@ */ class CacheTokenVerifier implements TokenVerifierInterface { - private $cache; - private $outdatedTokenTtl; - private $cacheKeyPrefix; + private CacheItemPoolInterface $cache; + private int $outdatedTokenTtl; + private string $cacheKeyPrefix; /** * @param int $outdatedTokenTtl How long the outdated token should still be considered valid. Defaults diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 8b0d932c..3e348df9 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -20,7 +20,7 @@ */ class InMemoryTokenProvider implements TokenProviderInterface { - private $tokens = []; + private array $tokens = []; /** * {@inheritdoc} diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index 42d22bea..b8b504b8 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -18,11 +18,11 @@ */ final class PersistentToken implements PersistentTokenInterface { - private $class; - private $userIdentifier; - private $series; - private $tokenValue; - private $lastUsed; + private string $class; + private string $userIdentifier; + private string $series; + private string $tokenValue; + private \DateTime $lastUsed; public function __construct(string $class, string $userIdentifier, string $series, string $tokenValue, \DateTime $lastUsed) { diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 42ede41c..e589c0f1 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -21,9 +21,9 @@ */ abstract class AbstractToken implements TokenInterface, \Serializable { - private $user; - private $roleNames = []; - private $attributes = []; + private ?UserInterface $user = null; + private array $roleNames = []; + private array $attributes = []; /** * @param string[] $roles An array of roles diff --git a/Authentication/Token/PreAuthenticatedToken.php b/Authentication/Token/PreAuthenticatedToken.php index 30579c52..da269b89 100644 --- a/Authentication/Token/PreAuthenticatedToken.php +++ b/Authentication/Token/PreAuthenticatedToken.php @@ -20,7 +20,7 @@ */ class PreAuthenticatedToken extends AbstractToken { - private $firewallName; + private string $firewallName; /** * @param string[] $roles diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index 06d2d363..d4d93690 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -20,8 +20,8 @@ */ class RememberMeToken extends AbstractToken { - private $secret; - private $firewallName; + private string $secret; + private string $firewallName; /** * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 5026d82b..fec307d9 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -24,8 +24,8 @@ */ class TokenStorage implements TokenStorageInterface, ResetInterface { - private $token; - private $initializer; + private ?TokenInterface $token = null; + private ?\Closure $initializer = null; /** * {@inheritdoc} @@ -56,7 +56,7 @@ public function setToken(TokenInterface $token = null) public function setInitializer(?callable $initializer): void { - $this->initializer = $initializer; + $this->initializer = null === $initializer || $initializer instanceof \Closure ? $initializer : \Closure::fromCallable($initializer); } public function reset() diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index 98686d28..46f6c96e 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -24,9 +24,9 @@ */ final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceSubscriberInterface { - private $storage; - private $container; - private $enableUsageTracking = false; + private TokenStorageInterface $storage; + private ContainerInterface $container; + private bool $enableUsageTracking = false; public function __construct(TokenStorageInterface $storage, ContainerInterface $container) { diff --git a/Authentication/Token/SwitchUserToken.php b/Authentication/Token/SwitchUserToken.php index 2c934f07..feb8c297 100644 --- a/Authentication/Token/SwitchUserToken.php +++ b/Authentication/Token/SwitchUserToken.php @@ -20,8 +20,8 @@ */ class SwitchUserToken extends UsernamePasswordToken { - private $originalToken; - private $originatedFromUri; + private TokenInterface $originalToken; + private ?string $originatedFromUri = null; /** * @param $user The username (like a nickname, email address, etc.), or a UserInterface instance or an object implementing a __toString method diff --git a/Authentication/Token/UsernamePasswordToken.php b/Authentication/Token/UsernamePasswordToken.php index 6dae8ec9..cd25b2b6 100644 --- a/Authentication/Token/UsernamePasswordToken.php +++ b/Authentication/Token/UsernamePasswordToken.php @@ -20,7 +20,7 @@ */ class UsernamePasswordToken extends AbstractToken { - private $firewallName; + private string $firewallName; public function __construct(UserInterface $user, string $firewallName, array $roles = []) { diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 31b9a6dd..19cebceb 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -25,9 +25,8 @@ */ class AuthorizationChecker implements AuthorizationCheckerInterface { - private $tokenStorage; - private $accessDecisionManager; - private $exceptionOnNoToken; + private TokenStorageInterface $tokenStorage; + private AccessDecisionManagerInterface $accessDecisionManager; public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, bool $exceptionOnNoToken = false) { @@ -37,7 +36,6 @@ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionM $this->tokenStorage = $tokenStorage; $this->accessDecisionManager = $accessDecisionManager; - $this->exceptionOnNoToken = $exceptionOnNoToken; } /** diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 5fbba9b9..426f71ba 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Authorization; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; /** @@ -24,11 +25,11 @@ */ class TraceableAccessDecisionManager implements AccessDecisionManagerInterface { - private $manager; - private $strategy; - private $voters = []; - private $decisionLog = []; // All decision logs - private $currentLog = []; // Logs being filled in + private AccessDecisionManagerInterface $manager; + private AccessDecisionStrategyInterface $strategy; + private iterable $voters = []; + private array $decisionLog = []; // All decision logs + private array $currentLog = []; // Logs being filled in public function __construct(AccessDecisionManagerInterface $manager) { diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index 1f04b911..bc72bb81 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -33,7 +33,7 @@ class AuthenticatedVoter implements CacheableVoterInterface public const IS_REMEMBERED = 'IS_REMEMBERED'; public const PUBLIC_ACCESS = 'PUBLIC_ACCESS'; - private $authenticationTrustResolver; + private AuthenticationTrustResolverInterface $authenticationTrustResolver; public function __construct(AuthenticationTrustResolverInterface $authenticationTrustResolver) { diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index 580ddc39..646da715 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -26,10 +26,10 @@ */ class ExpressionVoter implements VoterInterface { - private $expressionLanguage; - private $trustResolver; - private $authChecker; - private $roleHierarchy; + private ExpressionLanguage $expressionLanguage; + private AuthenticationTrustResolverInterface $trustResolver; + private AuthorizationCheckerInterface $authChecker; + private ?RoleHierarchyInterface $roleHierarchy; public function __construct(ExpressionLanguage $expressionLanguage, AuthenticationTrustResolverInterface $trustResolver, AuthorizationCheckerInterface $authChecker, RoleHierarchyInterface $roleHierarchy = null) { diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php index d8f2b34c..11c44138 100644 --- a/Authorization/Voter/RoleHierarchyVoter.php +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -22,7 +22,7 @@ */ class RoleHierarchyVoter extends RoleVoter { - private $roleHierarchy; + private RoleHierarchyInterface $roleHierarchy; public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefix = 'ROLE_') { diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index 703fdd25..b751a31e 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -20,7 +20,7 @@ */ class RoleVoter implements CacheableVoterInterface { - private $prefix; + private string $prefix; public function __construct(string $prefix = 'ROLE_') { diff --git a/Authorization/Voter/TraceableVoter.php b/Authorization/Voter/TraceableVoter.php index 0ce25ef2..412bb976 100644 --- a/Authorization/Voter/TraceableVoter.php +++ b/Authorization/Voter/TraceableVoter.php @@ -24,8 +24,8 @@ */ class TraceableVoter implements CacheableVoterInterface { - private $voter; - private $eventDispatcher; + private VoterInterface $voter; + private EventDispatcherInterface $eventDispatcher; public function __construct(VoterInterface $voter, EventDispatcherInterface $eventDispatcher) { diff --git a/Event/AuthenticationEvent.php b/Event/AuthenticationEvent.php index 4fc15196..1b9269fe 100644 --- a/Event/AuthenticationEvent.php +++ b/Event/AuthenticationEvent.php @@ -21,7 +21,7 @@ */ class AuthenticationEvent extends Event { - private $authenticationToken; + private TokenInterface $authenticationToken; public function __construct(TokenInterface $token) { diff --git a/Event/VoteEvent.php b/Event/VoteEvent.php index ef2756fc..1b1d6a33 100644 --- a/Event/VoteEvent.php +++ b/Event/VoteEvent.php @@ -23,10 +23,10 @@ */ final class VoteEvent extends Event { - private $voter; - private $subject; - private $attributes; - private $vote; + private VoterInterface $voter; + private mixed $subject; + private array $attributes; + private int $vote; public function __construct(VoterInterface $voter, mixed $subject, array $attributes, int $vote) { diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index 1ad76afc..79fb6eb6 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -18,8 +18,8 @@ */ class AccessDeniedException extends RuntimeException { - private $attributes = []; - private $subject; + private array $attributes = []; + private mixed $subject = null; public function __construct(string $message = 'Access Denied.', \Throwable $previous = null) { diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index 919dce16..0492c554 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -22,7 +22,7 @@ */ abstract class AccountStatusException extends AuthenticationException { - private $user; + private UserInterface $user; /** * Get the user. diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index 606c812f..e16dc0e7 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -21,7 +21,7 @@ */ class AuthenticationException extends RuntimeException { - private $token; + private ?TokenInterface $token = null; public function getToken(): ?TokenInterface { diff --git a/Exception/CustomUserMessageAccountStatusException.php b/Exception/CustomUserMessageAccountStatusException.php index 8bf27f5e..569cfa0d 100644 --- a/Exception/CustomUserMessageAccountStatusException.php +++ b/Exception/CustomUserMessageAccountStatusException.php @@ -23,9 +23,8 @@ */ class CustomUserMessageAccountStatusException extends AccountStatusException { - private $messageKey; - - private $messageData = []; + private string $messageKey; + private array $messageData = []; public function __construct(string $message = '', array $messageData = [], int $code = 0, \Throwable $previous = null) { diff --git a/Exception/CustomUserMessageAuthenticationException.php b/Exception/CustomUserMessageAuthenticationException.php index 97e45f00..42f9c4df 100644 --- a/Exception/CustomUserMessageAuthenticationException.php +++ b/Exception/CustomUserMessageAuthenticationException.php @@ -22,9 +22,8 @@ */ class CustomUserMessageAuthenticationException extends AuthenticationException { - private $messageKey; - - private $messageData = []; + private string $messageKey; + private array $messageData = []; public function __construct(string $message = '', array $messageData = [], int $code = 0, \Throwable $previous = null) { diff --git a/Exception/LazyResponseException.php b/Exception/LazyResponseException.php index 8edc248a..e26a3347 100644 --- a/Exception/LazyResponseException.php +++ b/Exception/LazyResponseException.php @@ -20,7 +20,7 @@ */ class LazyResponseException extends \Exception implements ExceptionInterface { - private $response; + private Response $response; public function __construct(Response $response) { diff --git a/Exception/TooManyLoginAttemptsAuthenticationException.php b/Exception/TooManyLoginAttemptsAuthenticationException.php index 0df80e5e..ffd71149 100644 --- a/Exception/TooManyLoginAttemptsAuthenticationException.php +++ b/Exception/TooManyLoginAttemptsAuthenticationException.php @@ -19,7 +19,7 @@ */ class TooManyLoginAttemptsAuthenticationException extends AuthenticationException { - private $threshold; + private ?int $threshold; public function __construct(int $threshold = null) { diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 5da4f0e4..98ebf538 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -19,7 +19,7 @@ */ class UserNotFoundException extends AuthenticationException { - private $identifier; + private ?string $identifier = null; /** * {@inheritdoc} diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index 76a5548d..8073c489 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -18,12 +18,9 @@ */ class RoleHierarchy implements RoleHierarchyInterface { - private $hierarchy; + private array $hierarchy; protected $map; - /** - * @param array $hierarchy An array defining the hierarchy - */ public function __construct(array $hierarchy) { $this->hierarchy = $hierarchy; diff --git a/Security.php b/Security.php index ccd42813..3c7c9473 100644 --- a/Security.php +++ b/Security.php @@ -28,7 +28,7 @@ class Security implements AuthorizationCheckerInterface public const LAST_USERNAME = '_security.last_username'; public const MAX_USERNAME_LENGTH = 4096; - private $container; + private ContainerInterface $container; public function __construct(ContainerInterface $container) { diff --git a/Signature/ExpiredSignatureStorage.php b/Signature/ExpiredSignatureStorage.php index 5421c77e..402092f6 100644 --- a/Signature/ExpiredSignatureStorage.php +++ b/Signature/ExpiredSignatureStorage.php @@ -20,8 +20,8 @@ */ final class ExpiredSignatureStorage { - private $cache; - private $lifetime; + private CacheItemPoolInterface $cache; + private int $lifetime; public function __construct(CacheItemPoolInterface $cache, int $lifetime) { diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index ec252e26..826286dd 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -24,11 +24,11 @@ */ class SignatureHasher { - private $propertyAccessor; - private $signatureProperties; - private $secret; - private $expiredSignaturesStorage; - private $maxUses; + private PropertyAccessorInterface $propertyAccessor; + private array $signatureProperties; + private string $secret; + private ?ExpiredSignatureStorage $expiredSignaturesStorage; + private ?int $maxUses; /** * @param array $signatureProperties properties of the User; the hash is invalidated if these properties change diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index e0e8c6e7..71bed2fd 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -24,10 +24,10 @@ */ class ChainUserProvider implements UserProviderInterface, PasswordUpgraderInterface { - private $providers; + private iterable $providers; /** - * @param iterable|UserProviderInterface[] $providers + * @param iterable $providers */ public function __construct(iterable $providers) { diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index 405aaeee..1a506749 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -21,10 +21,10 @@ */ final class InMemoryUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface { - private $username; - private $password; - private $enabled; - private $roles; + private string $username; + private ?string $password; + private bool $enabled; + private array $roles; public function __construct(?string $username, ?string $password, array $roles = [], bool $enabled = true) { diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index bd813d55..5da10cc9 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -24,7 +24,10 @@ */ class InMemoryUserProvider implements UserProviderInterface { - private $users; + /** + * @var array + */ + private array $users = []; /** * The user array is a hash where the keys are usernames and the values are diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 42b13921..178cf771 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -22,8 +22,8 @@ class UserPasswordValidator extends ConstraintValidator { - private $tokenStorage; - private $hasherFactory; + private TokenStorageInterface $tokenStorage; + private PasswordHasherFactoryInterface $hasherFactory; public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherFactoryInterface $hasherFactory) { From ed08e21cb025176d7977f57781c3772010e6c5b3 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Sat, 20 Nov 2021 19:48:49 +0100 Subject: [PATCH 028/138] [Tests] Remove some unused fixtures --- .../Token/AbstractTokenTest.php | 60 ------------------- Tests/SecurityTest.php | 8 --- 2 files changed, 68 deletions(-) diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index 03cc6d17..056123f6 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -94,66 +94,6 @@ public function testSetUser($user) } } -class TestUser -{ - protected $name; - - public function __construct($name) - { - $this->name = $name; - } - - public function __toString(): string - { - return $this->name; - } -} - -class SerializableUser implements UserInterface -{ - private $roles; - private $name; - - public function __construct($name, array $roles = []) - { - $this->name = $name; - $this->roles = $roles; - } - - public function getUsername(): string - { - return $this->name; - } - - public function getUserIdentifier(): string - { - return $this->name; - } - - public function getPassword(): ?string - { - return '***'; - } - - public function getRoles(): array - { - if (empty($this->roles)) { - return ['ROLE_USER']; - } - - return $this->roles; - } - - public function eraseCredentials() - { - } - - public function getSalt(): ?string - { - return null; - } -} - class ConcreteToken extends AbstractToken { private $credentials = 'credentials_value'; diff --git a/Tests/SecurityTest.php b/Tests/SecurityTest.php index 53dd23b2..064998f9 100644 --- a/Tests/SecurityTest.php +++ b/Tests/SecurityTest.php @@ -93,11 +93,3 @@ private function createContainer($serviceId, $serviceObject) return $container; } } - -class StringishUser -{ - public function __toString(): string - { - return 'stringish_user'; - } -} From 77d7dc9b279b28a99dfb7f9ec02efcbdeee13d7e Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Wed, 8 Dec 2021 14:13:04 +0100 Subject: [PATCH 029/138] Leverage str_starts_with(), str_ends_with() and str_contains() --- Authorization/Voter/Voter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authorization/Voter/Voter.php b/Authorization/Voter/Voter.php index a206c387..2382b72f 100644 --- a/Authorization/Voter/Voter.php +++ b/Authorization/Voter/Voter.php @@ -35,7 +35,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes): continue; } } catch (\TypeError $e) { - if (false !== strpos($e->getMessage(), 'supports(): Argument #1')) { + if (str_contains($e->getMessage(), 'supports(): Argument #1')) { continue; } From 10cce36a20bed3f41e5cd24faed8f7d0795bab62 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 7 Dec 2021 12:27:08 +0100 Subject: [PATCH 030/138] Remove FQCN type hints on properties --- Authentication/RememberMe/CacheTokenVerifier.php | 2 +- Authentication/Token/AbstractToken.php | 2 +- Authentication/Token/Storage/TokenStorage.php | 2 +- .../Token/Storage/UsageTrackingTokenStorage.php | 4 ++-- Authentication/Token/SwitchUserToken.php | 2 +- Authorization/AccessDecisionManager.php | 2 +- Authorization/AuthorizationChecker.php | 4 ++-- Authorization/TraceableAccessDecisionManager.php | 5 ++--- Authorization/Voter/AuthenticatedVoter.php | 2 +- Authorization/Voter/ExpressionVoter.php | 8 ++++---- Authorization/Voter/RoleHierarchyVoter.php | 2 +- Authorization/Voter/TraceableVoter.php | 4 ++-- Event/AuthenticationEvent.php | 2 +- Event/VoteEvent.php | 2 +- Exception/AccountStatusException.php | 2 +- Exception/AuthenticationException.php | 2 +- Exception/LazyResponseException.php | 2 +- Security.php | 2 +- Signature/ExpiredSignatureStorage.php | 2 +- Signature/SignatureHasher.php | 4 ++-- Validator/Constraints/UserPasswordValidator.php | 4 ++-- 21 files changed, 30 insertions(+), 31 deletions(-) diff --git a/Authentication/RememberMe/CacheTokenVerifier.php b/Authentication/RememberMe/CacheTokenVerifier.php index 55cbdcf4..83c21ea4 100644 --- a/Authentication/RememberMe/CacheTokenVerifier.php +++ b/Authentication/RememberMe/CacheTokenVerifier.php @@ -18,7 +18,7 @@ */ class CacheTokenVerifier implements TokenVerifierInterface { - private CacheItemPoolInterface $cache; + private $cache; private int $outdatedTokenTtl; private string $cacheKeyPrefix; diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index e589c0f1..4bd4ffde 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -21,7 +21,7 @@ */ abstract class AbstractToken implements TokenInterface, \Serializable { - private ?UserInterface $user = null; + private $user = null; private array $roleNames = []; private array $attributes = []; diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index fec307d9..55616c2b 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -24,7 +24,7 @@ */ class TokenStorage implements TokenStorageInterface, ResetInterface { - private ?TokenInterface $token = null; + private $token = null; private ?\Closure $initializer = null; /** diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index 46f6c96e..efb7faef 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -24,8 +24,8 @@ */ final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceSubscriberInterface { - private TokenStorageInterface $storage; - private ContainerInterface $container; + private $storage; + private $container; private bool $enableUsageTracking = false; public function __construct(TokenStorageInterface $storage, ContainerInterface $container) diff --git a/Authentication/Token/SwitchUserToken.php b/Authentication/Token/SwitchUserToken.php index feb8c297..636be4ca 100644 --- a/Authentication/Token/SwitchUserToken.php +++ b/Authentication/Token/SwitchUserToken.php @@ -20,7 +20,7 @@ */ class SwitchUserToken extends UsernamePasswordToken { - private TokenInterface $originalToken; + private $originalToken; private ?string $originatedFromUri = null; /** diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index eccbec9b..1ecb390f 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -35,7 +35,7 @@ final class AccessDecisionManager implements AccessDecisionManagerInterface private iterable $voters; private array $votersCacheAttributes = []; private array $votersCacheObject = []; - private AccessDecisionStrategyInterface $strategy; + private $strategy; /** * @param iterable $voters An array or an iterator of VoterInterface instances diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 19cebceb..23a01585 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -25,8 +25,8 @@ */ class AuthorizationChecker implements AuthorizationCheckerInterface { - private TokenStorageInterface $tokenStorage; - private AccessDecisionManagerInterface $accessDecisionManager; + private $tokenStorage; + private $accessDecisionManager; public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, bool $exceptionOnNoToken = false) { diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index a044ae74..7a85198d 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Core\Authorization; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; /** @@ -25,8 +24,8 @@ */ class TraceableAccessDecisionManager implements AccessDecisionManagerInterface { - private AccessDecisionManagerInterface $manager; - private AccessDecisionStrategyInterface $strategy; + private $manager; + private $strategy; /** @var iterable */ private iterable $voters = []; private array $decisionLog = []; // All decision logs diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index bc72bb81..1f04b911 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -33,7 +33,7 @@ class AuthenticatedVoter implements CacheableVoterInterface public const IS_REMEMBERED = 'IS_REMEMBERED'; public const PUBLIC_ACCESS = 'PUBLIC_ACCESS'; - private AuthenticationTrustResolverInterface $authenticationTrustResolver; + private $authenticationTrustResolver; public function __construct(AuthenticationTrustResolverInterface $authenticationTrustResolver) { diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index 3f50dccc..84c2d76b 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -26,10 +26,10 @@ */ class ExpressionVoter implements CacheableVoterInterface { - private ExpressionLanguage $expressionLanguage; - private AuthenticationTrustResolverInterface $trustResolver; - private AuthorizationCheckerInterface $authChecker; - private ?RoleHierarchyInterface $roleHierarchy; + private $expressionLanguage; + private $trustResolver; + private $authChecker; + private $roleHierarchy; public function __construct(ExpressionLanguage $expressionLanguage, AuthenticationTrustResolverInterface $trustResolver, AuthorizationCheckerInterface $authChecker, RoleHierarchyInterface $roleHierarchy = null) { diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php index 11c44138..d8f2b34c 100644 --- a/Authorization/Voter/RoleHierarchyVoter.php +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -22,7 +22,7 @@ */ class RoleHierarchyVoter extends RoleVoter { - private RoleHierarchyInterface $roleHierarchy; + private $roleHierarchy; public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefix = 'ROLE_') { diff --git a/Authorization/Voter/TraceableVoter.php b/Authorization/Voter/TraceableVoter.php index 412bb976..0ce25ef2 100644 --- a/Authorization/Voter/TraceableVoter.php +++ b/Authorization/Voter/TraceableVoter.php @@ -24,8 +24,8 @@ */ class TraceableVoter implements CacheableVoterInterface { - private VoterInterface $voter; - private EventDispatcherInterface $eventDispatcher; + private $voter; + private $eventDispatcher; public function __construct(VoterInterface $voter, EventDispatcherInterface $eventDispatcher) { diff --git a/Event/AuthenticationEvent.php b/Event/AuthenticationEvent.php index 1b9269fe..4fc15196 100644 --- a/Event/AuthenticationEvent.php +++ b/Event/AuthenticationEvent.php @@ -21,7 +21,7 @@ */ class AuthenticationEvent extends Event { - private TokenInterface $authenticationToken; + private $authenticationToken; public function __construct(TokenInterface $token) { diff --git a/Event/VoteEvent.php b/Event/VoteEvent.php index 1b1d6a33..82bda499 100644 --- a/Event/VoteEvent.php +++ b/Event/VoteEvent.php @@ -23,7 +23,7 @@ */ final class VoteEvent extends Event { - private VoterInterface $voter; + private $voter; private mixed $subject; private array $attributes; private int $vote; diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index 0492c554..919dce16 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -22,7 +22,7 @@ */ abstract class AccountStatusException extends AuthenticationException { - private UserInterface $user; + private $user; /** * Get the user. diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index 298bc78c..9e2b02b6 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -24,7 +24,7 @@ class AuthenticationException extends RuntimeException /** @internal */ protected $serialized; - private ?TokenInterface $token = null; + private $token = null; public function __construct(string $message = '', int $code = 0, \Throwable $previous = null) { diff --git a/Exception/LazyResponseException.php b/Exception/LazyResponseException.php index e26a3347..8edc248a 100644 --- a/Exception/LazyResponseException.php +++ b/Exception/LazyResponseException.php @@ -20,7 +20,7 @@ */ class LazyResponseException extends \Exception implements ExceptionInterface { - private Response $response; + private $response; public function __construct(Response $response) { diff --git a/Security.php b/Security.php index 3c7c9473..ccd42813 100644 --- a/Security.php +++ b/Security.php @@ -28,7 +28,7 @@ class Security implements AuthorizationCheckerInterface public const LAST_USERNAME = '_security.last_username'; public const MAX_USERNAME_LENGTH = 4096; - private ContainerInterface $container; + private $container; public function __construct(ContainerInterface $container) { diff --git a/Signature/ExpiredSignatureStorage.php b/Signature/ExpiredSignatureStorage.php index 402092f6..593be77c 100644 --- a/Signature/ExpiredSignatureStorage.php +++ b/Signature/ExpiredSignatureStorage.php @@ -20,7 +20,7 @@ */ final class ExpiredSignatureStorage { - private CacheItemPoolInterface $cache; + private $cache; private int $lifetime; public function __construct(CacheItemPoolInterface $cache, int $lifetime) diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index 826286dd..87d653d9 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -24,10 +24,10 @@ */ class SignatureHasher { - private PropertyAccessorInterface $propertyAccessor; + private $propertyAccessor; private array $signatureProperties; private string $secret; - private ?ExpiredSignatureStorage $expiredSignaturesStorage; + private $expiredSignaturesStorage; private ?int $maxUses; /** diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 178cf771..42b13921 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -22,8 +22,8 @@ class UserPasswordValidator extends ConstraintValidator { - private TokenStorageInterface $tokenStorage; - private PasswordHasherFactoryInterface $hasherFactory; + private $tokenStorage; + private $hasherFactory; public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherFactoryInterface $hasherFactory) { From 6c0507b5ef420301d9eef25f40cd79c1181fe5ff Mon Sep 17 00:00:00 2001 From: helmi <43951764+helmis123@users.noreply.github.com> Date: Mon, 27 Dec 2021 16:20:20 +0100 Subject: [PATCH 031/138] A sprintf is missing A sprintf is missing when throw a LogicException in the constructor of AuthorizationChecker class. --- Authorization/AuthorizationChecker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 23a01585..5946060f 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -31,7 +31,7 @@ class AuthorizationChecker implements AuthorizationCheckerInterface public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, bool $exceptionOnNoToken = false) { if ($exceptionOnNoToken) { - throw new \LogicException('Argument $exceptionOnNoToken of "%s()" must be set to "false".', __METHOD__); + throw new \LogicException(sprintf('Argument $exceptionOnNoToken of "%s()" must be set to "false".', __METHOD__)); } $this->tokenStorage = $tokenStorage; From 26dd796d7f48a44de233a0be95327683f354d96a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Dec 2021 10:11:47 +0100 Subject: [PATCH 032/138] [Security] fix compat with older session payloads --- Authentication/Token/AbstractToken.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 4bd4ffde..2d6139c8 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Authentication\Token; use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\InMemoryUser; /** * Base class for Token instances. @@ -114,7 +115,8 @@ public function __serialize(): array */ public function __unserialize(array $data): void { - [$this->user, , , $this->attributes, $this->roleNames] = $data; + [$user, , , $this->attributes, $this->roleNames] = $data; + $this->user = \is_string($user) ? new InMemoryUser($user, '', $this->roleNames) : $user; } /** From 5580d791d999ae500367780612c6210f03716b6c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Dec 2021 11:31:56 +0100 Subject: [PATCH 033/138] [Security] consider old session payloads as unauthenticated --- Authentication/Token/AbstractToken.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 2d6139c8..5eff2eff 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -116,7 +116,7 @@ public function __serialize(): array public function __unserialize(array $data): void { [$user, , , $this->attributes, $this->roleNames] = $data; - $this->user = \is_string($user) ? new InMemoryUser($user, '', $this->roleNames) : $user; + $this->user = \is_string($user) ? new InMemoryUser($user, '', $this->roleNames, false) : $user; } /** From 8c83a376fe39170b59dd610cfb74ef4889df7d7d Mon Sep 17 00:00:00 2001 From: javer Date: Thu, 13 Jan 2022 19:52:47 +0200 Subject: [PATCH 034/138] Fix retrieving strategy for custom AccessDecisionManager --- Authorization/TraceableAccessDecisionManager.php | 2 +- .../Authorization/TraceableAccessDecisionManagerTest.php | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index a044ae74..e4acd490 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -26,7 +26,7 @@ class TraceableAccessDecisionManager implements AccessDecisionManagerInterface { private AccessDecisionManagerInterface $manager; - private AccessDecisionStrategyInterface $strategy; + private ?AccessDecisionStrategyInterface $strategy = null; /** @var iterable */ private iterable $voters = []; private array $decisionLog = []; // All decision logs diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index 4b97885e..53d33726 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -274,4 +274,13 @@ public function testAccessDecisionManagerCalledByVoter() ], ], $sut->getDecisionLog()); } + + public function testCustomAccessDecisionManagerReturnsEmptyStrategy() + { + $admMock = $this->createMock(AccessDecisionManagerInterface::class); + + $adm = new TraceableAccessDecisionManager($admMock); + + $this->assertEquals('-', $adm->getStrategy()); + } } From f7c62d741b53d8d4aa5a176365bdd6966214d06b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 9 Feb 2022 15:00:38 +0100 Subject: [PATCH 035/138] Bump minimum version of PHP to 8.1 --- Authentication/Token/Storage/TokenStorage.php | 2 +- Authorization/TraceableAccessDecisionManager.php | 2 -- composer.json | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index fec307d9..72450dc9 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -56,7 +56,7 @@ public function setToken(TokenInterface $token = null) public function setInitializer(?callable $initializer): void { - $this->initializer = null === $initializer || $initializer instanceof \Closure ? $initializer : \Closure::fromCallable($initializer); + $this->initializer = null === $initializer ? null : $initializer(...); } public function reset() diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index e4acd490..e1bd49a9 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -39,10 +39,8 @@ public function __construct(AccessDecisionManagerInterface $manager) if ($this->manager instanceof AccessDecisionManager) { // The strategy and voters are stored in a private properties of the decorated service $reflection = new \ReflectionProperty(AccessDecisionManager::class, 'strategy'); - $reflection->setAccessible(true); $this->strategy = $reflection->getValue($manager); $reflection = new \ReflectionProperty(AccessDecisionManager::class, 'voters'); - $reflection->setAccessible(true); $this->voters = $reflection->getValue($manager); } } diff --git a/composer.json b/composer.json index 20c5074d..7e5f8679 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^1.1|^2|^3", "symfony/service-contracts": "^1.1.6|^2|^3", "symfony/password-hasher": "^5.4|^6.0" From 6bac15fa1c385802c78176fe11e752ee11d42148 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 31 Mar 2022 10:18:50 +0200 Subject: [PATCH 036/138] [Messenger][Security/Core] Remove legacy class aliases --- Authorization/TraceableAccessDecisionManager.php | 4 ---- Exception/UserNotFoundException.php | 4 ---- .../Authorization/TraceableAccessDecisionManagerTest.php | 8 -------- 3 files changed, 16 deletions(-) diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 7a85198d..3a44f67a 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -109,7 +109,3 @@ public function getDecisionLog(): array return $this->decisionLog; } } - -if (!class_exists(DebugAccessDecisionManager::class, false)) { - class_alias(TraceableAccessDecisionManager::class, DebugAccessDecisionManager::class); -} diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 98ebf538..17f488b6 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -71,7 +71,3 @@ public function __unserialize(array $data): void parent::__unserialize($parentData); } } - -if (!class_exists(UsernameNotFoundException::class, false)) { - class_alias(UserNotFoundException::class, UsernameNotFoundException::class); -} diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index 4b97885e..30d2dfd3 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -15,7 +15,6 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; -use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; @@ -171,13 +170,6 @@ public function provideObjectsAndLogs(): \Generator ]; } - public function testDebugAccessDecisionManagerAliasExistsForBC() - { - $adm = new TraceableAccessDecisionManager(new AccessDecisionManager()); - - $this->assertInstanceOf(DebugAccessDecisionManager::class, $adm, 'For BC, TraceableAccessDecisionManager must be an instance of DebugAccessDecisionManager'); - } - /** * Tests decision log returned when a voter call decide method of AccessDecisionManager. */ From eb2967d9dd2cd5d7ea0ca88cdbde84bb1c9f7e28 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 31 Mar 2022 19:20:31 +0200 Subject: [PATCH 037/138] Revert "minor #45899 [Messenger] [Security/Core] Remove legacy class aliases (nicolas-grekas)" This reverts commit 758ed6bf66648a17432496162cd02215b8809df4, reversing changes made to 1bd3af4ec6303a4cf3de0218abc29ca9fdd7a793. --- Authorization/TraceableAccessDecisionManager.php | 4 ++++ Exception/UserNotFoundException.php | 4 ++++ .../Authorization/TraceableAccessDecisionManagerTest.php | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 3a44f67a..7a85198d 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -109,3 +109,7 @@ public function getDecisionLog(): array return $this->decisionLog; } } + +if (!class_exists(DebugAccessDecisionManager::class, false)) { + class_alias(TraceableAccessDecisionManager::class, DebugAccessDecisionManager::class); +} diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 17f488b6..98ebf538 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -71,3 +71,7 @@ public function __unserialize(array $data): void parent::__unserialize($parentData); } } + +if (!class_exists(UsernameNotFoundException::class, false)) { + class_alias(UserNotFoundException::class, UsernameNotFoundException::class); +} diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index 30d2dfd3..4b97885e 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; +use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; @@ -170,6 +171,13 @@ public function provideObjectsAndLogs(): \Generator ]; } + public function testDebugAccessDecisionManagerAliasExistsForBC() + { + $adm = new TraceableAccessDecisionManager(new AccessDecisionManager()); + + $this->assertInstanceOf(DebugAccessDecisionManager::class, $adm, 'For BC, TraceableAccessDecisionManager must be an instance of DebugAccessDecisionManager'); + } + /** * Tests decision log returned when a voter call decide method of AccessDecisionManager. */ From 9aeb160b9de110aa2e06dcc8f59def7049ce887b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 31 Mar 2022 19:24:08 +0200 Subject: [PATCH 038/138] Restore "minor #45899 [Messenger] [Security/Core] Remove legacy class aliases (nicolas-grekas)" This reverts commit 235262eebd111b224b583564e74c423c1acd31cb. --- Authorization/TraceableAccessDecisionManager.php | 4 ---- Exception/UserNotFoundException.php | 4 ---- .../Authorization/TraceableAccessDecisionManagerTest.php | 8 -------- 3 files changed, 16 deletions(-) diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index e1bd49a9..30d5de8f 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -108,7 +108,3 @@ public function getDecisionLog(): array return $this->decisionLog; } } - -if (!class_exists(DebugAccessDecisionManager::class, false)) { - class_alias(TraceableAccessDecisionManager::class, DebugAccessDecisionManager::class); -} diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 98ebf538..17f488b6 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -71,7 +71,3 @@ public function __unserialize(array $data): void parent::__unserialize($parentData); } } - -if (!class_exists(UsernameNotFoundException::class, false)) { - class_alias(UserNotFoundException::class, UsernameNotFoundException::class); -} diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index 53d33726..c0ba6f11 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -15,7 +15,6 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; -use Symfony\Component\Security\Core\Authorization\DebugAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; @@ -171,13 +170,6 @@ public function provideObjectsAndLogs(): \Generator ]; } - public function testDebugAccessDecisionManagerAliasExistsForBC() - { - $adm = new TraceableAccessDecisionManager(new AccessDecisionManager()); - - $this->assertInstanceOf(DebugAccessDecisionManager::class, $adm, 'For BC, TraceableAccessDecisionManager must be an instance of DebugAccessDecisionManager'); - } - /** * Tests decision log returned when a voter call decide method of AccessDecisionManager. */ From ca0aaa73fb1a1d5242e206f428e2f24d7c364a9f Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Thu, 31 Mar 2022 18:23:12 +0200 Subject: [PATCH 039/138] Leverage non-capturing catches --- User/ChainUserProvider.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index d5a868b6..29a6c4b6 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -59,7 +59,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface foreach ($this->providers as $provider) { try { return $provider->loadUserByIdentifier($identifier); - } catch (UserNotFoundException $e) { + } catch (UserNotFoundException) { // try next one } } @@ -83,9 +83,9 @@ public function refreshUser(UserInterface $user): UserInterface } return $provider->refreshUser($user); - } catch (UnsupportedUserException $e) { + } catch (UnsupportedUserException) { // try next one - } catch (UserNotFoundException $e) { + } catch (UserNotFoundException) { $supportedUserFound = true; // try next one } @@ -124,7 +124,7 @@ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string if ($provider instanceof PasswordUpgraderInterface) { try { $provider->upgradePassword($user, $newHashedPassword); - } catch (UnsupportedUserException $e) { + } catch (UnsupportedUserException) { // ignore: password upgrades are opportunistic } } From 5dc2a87f4522c6ff4b034e5ac495f2c3fbb6e867 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 11 May 2022 14:12:29 +0200 Subject: [PATCH 040/138] Update sponsors of components v6.1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e31770c..00e74cba 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 5.4/6.0 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.1 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video From 70f297c9c65db039c7b3df963dfcd963575e133e Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Mon, 18 Apr 2022 00:26:17 +0200 Subject: [PATCH 041/138] [Security] Move the `Security` helper to SecurityBundle --- CHANGELOG.md | 5 +++++ Security.php | 32 ++++++++++++++++++++++++++++++-- Tests/SecurityTest.php | 3 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c30f0fca..92f4ccca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.2 +--- + +* Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead + 6.0 --- diff --git a/Security.php b/Security.php index 3c7c9473..d50e37b5 100644 --- a/Security.php +++ b/Security.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core; use Psr\Container\ContainerInterface; +use Symfony\Bundle\SecurityBundle\Security\Security as NewSecurityHelper; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -19,20 +20,47 @@ /** * Helper class for commonly-needed security tasks. * - * @final + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security instead */ class Security implements AuthorizationCheckerInterface { + /** + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security::ACCESS_DENIED_ERROR instead + * + * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:ACCESS_DENIED_ERROR. + */ public const ACCESS_DENIED_ERROR = '_security.403_error'; + + /** + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security::AUTHENTICATION_ERROR instead + * + * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:AUTHENTICATION_ERROR. + */ public const AUTHENTICATION_ERROR = '_security.last_error'; + + /** + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security::LAST_USERNAME instead + * + * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:LAST_USERNAME. + */ public const LAST_USERNAME = '_security.last_username'; + + /** + * @deprecated since Symfony 6.2, use \Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface::MAX_USERNAME_LENGTH instead + * + * In 7.0, move this constant to the NewSecurityHelper class and make it reference AuthenticatorInterface:MAX_USERNAME_LENGTH. + */ public const MAX_USERNAME_LENGTH = 4096; private ContainerInterface $container; - public function __construct(ContainerInterface $container) + public function __construct(ContainerInterface $container, bool $triggerDeprecation = true) { $this->container = $container; + + if ($triggerDeprecation) { + trigger_deprecation('symfony/security-core', '6.2', 'The "%s" class is deprecated, use "%s" instead.', __CLASS__, NewSecurityHelper::class); + } } public function getUser(): ?UserInterface diff --git a/Tests/SecurityTest.php b/Tests/SecurityTest.php index 064998f9..63eca289 100644 --- a/Tests/SecurityTest.php +++ b/Tests/SecurityTest.php @@ -20,6 +20,9 @@ use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\User\InMemoryUser; +/** + * @group legacy + */ class SecurityTest extends TestCase { public function testGetToken() From 4ebb6f393e31c04d1407d0d2b619924b5534184a Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Wed, 8 Jun 2022 00:24:18 +0200 Subject: [PATCH 042/138] [Security] Centralize max username length enforcement --- Security.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Security.php b/Security.php index d50e37b5..c57c506a 100644 --- a/Security.php +++ b/Security.php @@ -46,9 +46,7 @@ class Security implements AuthorizationCheckerInterface public const LAST_USERNAME = '_security.last_username'; /** - * @deprecated since Symfony 6.2, use \Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface::MAX_USERNAME_LENGTH instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference AuthenticatorInterface:MAX_USERNAME_LENGTH. + * @deprecated since Symfony 6.2, use \Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge::MAX_USERNAME_LENGTH instead */ public const MAX_USERNAME_LENGTH = 4096; From 56e0bcb3ba556785e651fe1cb07603bd0f13039d Mon Sep 17 00:00:00 2001 From: Mark Ogilvie Date: Fri, 24 Jun 2022 12:57:32 +0100 Subject: [PATCH 043/138] Initially set user null. getUser is ?UserInterface return, but throws unset user exception. Typed property Symfony\Component\Security\Core\Exception\AccountStatusException::$user must not be accessed before initialization --- Exception/AccountStatusException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index b3263cbc..76878f9f 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -22,7 +22,7 @@ */ abstract class AccountStatusException extends AuthenticationException { - private UserInterface $user; + private ?UserInterface $user = null; /** * Get the user. From 21661fbcdb1c796e82f5a8ac7250bb835be3c374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Apr 2022 23:07:28 +0200 Subject: [PATCH 044/138] Hide sensitive information with `SensitiveParameter` attribute --- Authentication/RememberMe/CacheTokenVerifier.php | 4 ++-- Authentication/RememberMe/InMemoryTokenProvider.php | 2 +- Authentication/RememberMe/PersistentToken.php | 2 +- Authentication/RememberMe/TokenProviderInterface.php | 2 +- Authentication/RememberMe/TokenVerifierInterface.php | 4 ++-- Authentication/Token/RememberMeToken.php | 2 +- Signature/SignatureHasher.php | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Authentication/RememberMe/CacheTokenVerifier.php b/Authentication/RememberMe/CacheTokenVerifier.php index 65e15ead..a9af5927 100644 --- a/Authentication/RememberMe/CacheTokenVerifier.php +++ b/Authentication/RememberMe/CacheTokenVerifier.php @@ -38,7 +38,7 @@ public function __construct(CacheItemPoolInterface $cache, int $outdatedTokenTtl /** * {@inheritdoc} */ - public function verifyToken(PersistentTokenInterface $token, string $tokenValue): bool + public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue): bool { if (hash_equals($token->getTokenValue(), $tokenValue)) { return true; @@ -58,7 +58,7 @@ public function verifyToken(PersistentTokenInterface $token, string $tokenValue) /** * {@inheritdoc} */ - public function updateExistingToken(PersistentTokenInterface $token, string $tokenValue, \DateTimeInterface $lastUsed): void + public function updateExistingToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed): void { // When a token gets updated, persist the outdated token for $outdatedTokenTtl seconds so we can // still accept it as valid in verifyToken diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 3e348df9..e6d52661 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -37,7 +37,7 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface /** * {@inheritdoc} */ - public function updateToken(string $series, string $tokenValue, \DateTime $lastUsed) + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) { if (!isset($this->tokens[$series])) { throw new TokenNotFoundException('No token found.'); diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index b8b504b8..9f3f9e76 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -24,7 +24,7 @@ final class PersistentToken implements PersistentTokenInterface private string $tokenValue; private \DateTime $lastUsed; - public function __construct(string $class, string $userIdentifier, string $series, string $tokenValue, \DateTime $lastUsed) + public function __construct(string $class, string $userIdentifier, string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) { if (empty($class)) { throw new \InvalidArgumentException('$class must not be empty.'); diff --git a/Authentication/RememberMe/TokenProviderInterface.php b/Authentication/RememberMe/TokenProviderInterface.php index eda47300..9b32fdce 100644 --- a/Authentication/RememberMe/TokenProviderInterface.php +++ b/Authentication/RememberMe/TokenProviderInterface.php @@ -39,7 +39,7 @@ public function deleteTokenBySeries(string $series); * * @throws TokenNotFoundException if the token is not found */ - public function updateToken(string $series, string $tokenValue, \DateTime $lastUsed); + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed); /** * Creates a new token. diff --git a/Authentication/RememberMe/TokenVerifierInterface.php b/Authentication/RememberMe/TokenVerifierInterface.php index 57278d9e..a3231750 100644 --- a/Authentication/RememberMe/TokenVerifierInterface.php +++ b/Authentication/RememberMe/TokenVerifierInterface.php @@ -23,10 +23,10 @@ interface TokenVerifierInterface * * Do not forget to implement token comparisons using hash_equals for a secure implementation. */ - public function verifyToken(PersistentTokenInterface $token, string $tokenValue): bool; + public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue): bool; /** * Updates an existing token with a new token value and lastUsed time. */ - public function updateExistingToken(PersistentTokenInterface $token, string $tokenValue, \DateTimeInterface $lastUsed): void; + public function updateExistingToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed): void; } diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index d4d93690..147c1d68 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -28,7 +28,7 @@ class RememberMeToken extends AbstractToken * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, string $firewallName, string $secret) + public function __construct(UserInterface $user, string $firewallName, #[\SensitiveParameter] string $secret) { parent::__construct($user->getRoles()); diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index 04022d58..29d508ce 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -35,7 +35,7 @@ class SignatureHasher * @param ExpiredSignatureStorage|null $expiredSignaturesStorage if provided, secures a sequence of hashes that are expired * @param int|null $maxUses used together with $expiredSignatureStorage to allow a maximum usage of a hash */ - public function __construct(PropertyAccessorInterface $propertyAccessor, array $signatureProperties, string $secret, ExpiredSignatureStorage $expiredSignaturesStorage = null, int $maxUses = null) + public function __construct(PropertyAccessorInterface $propertyAccessor, array $signatureProperties, #[\SensitiveParameter] string $secret, ExpiredSignatureStorage $expiredSignaturesStorage = null, int $maxUses = null) { $this->propertyAccessor = $propertyAccessor; $this->signatureProperties = $signatureProperties; From a9f9ac8df6280bc1a5d57dbf57f2498357acac13 Mon Sep 17 00:00:00 2001 From: Maarten de Boer Date: Mon, 12 Apr 2021 17:21:52 +0200 Subject: [PATCH 045/138] Improve some PHPdocs based on existing Symfony stubs in PHPstan and Psalm --- Authorization/Voter/VoterInterface.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Authorization/Voter/VoterInterface.php b/Authorization/Voter/VoterInterface.php index 7e401c3f..ba52c8ea 100644 --- a/Authorization/Voter/VoterInterface.php +++ b/Authorization/Voter/VoterInterface.php @@ -34,6 +34,7 @@ interface VoterInterface * @param array $attributes An array of attributes associated with the method being invoked * * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED + * @psalm-return self::ACCESS_* must be transformed into @return on Symfony 7 */ public function vote(TokenInterface $token, mixed $subject, array $attributes); } From a2af1742104b8753b452458e63f12dfb73c72260 Mon Sep 17 00:00:00 2001 From: Laurent VOULLEMIER Date: Wed, 10 Aug 2022 11:32:19 +0200 Subject: [PATCH 046/138] Remove wrong PHPDoc isGranted doesn't throw anymore an exception when there is no token in the storage --- Authorization/AuthorizationChecker.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 5946060f..9bf138bc 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -40,8 +40,6 @@ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionM /** * {@inheritdoc} - * - * @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token and $exceptionOnNoToken is set to true */ final public function isGranted(mixed $attribute, mixed $subject = null): bool { From 29856bea6a5b841d041407a1cb57bb824a122160 Mon Sep 17 00:00:00 2001 From: Mathieu Piot Date: Fri, 19 Aug 2022 14:56:02 +0200 Subject: [PATCH 047/138] [Security][AbstractToken] getUserIdentifier() must return a string --- Authentication/Token/AbstractToken.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 1385a8ba..d08d382e 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -48,7 +48,7 @@ public function getRoleNames(): array public function getUserIdentifier(): string { - return $this->user->getUserIdentifier(); + return $this->user ? $this->user->getUserIdentifier() : ''; } /** From c38df8e21a31e50c2643e28086717efa38397179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 25 Aug 2022 16:59:21 +0200 Subject: [PATCH 048/138] [CS] Remove @inheritdoc PHPDoc --- .../AuthenticationTrustResolver.php | 6 ----- .../RememberMe/CacheTokenVerifier.php | 6 ----- .../RememberMe/InMemoryTokenProvider.php | 12 --------- Authentication/RememberMe/PersistentToken.php | 12 --------- Authentication/Token/AbstractToken.php | 27 ------------------- .../Token/PreAuthenticatedToken.php | 6 ----- Authentication/Token/RememberMeToken.php | 6 ----- Authentication/Token/Storage/TokenStorage.php | 6 ----- .../Storage/UsageTrackingTokenStorage.php | 6 ----- Authentication/Token/SwitchUserToken.php | 6 ----- .../Token/UsernamePasswordToken.php | 6 ----- Authorization/AccessDecisionManager.php | 2 -- Authorization/AuthorizationChecker.php | 3 --- Authorization/ExpressionLanguage.php | 3 --- .../Strategy/AffirmativeStrategy.php | 3 --- Authorization/Strategy/PriorityStrategy.php | 3 --- Authorization/Strategy/UnanimousStrategy.php | 3 --- .../TraceableAccessDecisionManager.php | 3 --- Authorization/Voter/AuthenticatedVoter.php | 3 --- Authorization/Voter/ExpressionVoter.php | 3 --- Authorization/Voter/RoleHierarchyVoter.php | 3 --- Authorization/Voter/RoleVoter.php | 3 --- Authorization/Voter/Voter.php | 3 --- Exception/AccountExpiredException.php | 3 --- Exception/AccountStatusException.php | 6 ----- ...enticationCredentialsNotFoundException.php | 3 --- Exception/AuthenticationExpiredException.php | 3 --- Exception/AuthenticationServiceException.php | 3 --- Exception/BadCredentialsException.php | 3 --- Exception/CookieTheftException.php | 3 --- Exception/CredentialsExpiredException.php | 3 --- ...ustomUserMessageAccountStatusException.php | 6 ----- ...stomUserMessageAuthenticationException.php | 6 ----- Exception/DisabledException.php | 3 --- .../InsufficientAuthenticationException.php | 3 --- Exception/InvalidCsrfTokenException.php | 3 --- Exception/LockedException.php | 3 --- Exception/ProviderNotFoundException.php | 3 --- Exception/SessionUnavailableException.php | 3 --- Exception/TokenNotFoundException.php | 3 --- ...nyLoginAttemptsAuthenticationException.php | 12 --------- Exception/UserNotFoundException.php | 12 --------- Role/RoleHierarchy.php | 3 --- User/ChainUserProvider.php | 9 ------- User/InMemoryUser.php | 12 --------- User/InMemoryUserProvider.php | 6 ----- User/MissingUserProvider.php | 9 ------- Validator/Constraints/UserPassword.php | 3 --- .../Constraints/UserPasswordValidator.php | 3 --- 49 files changed, 263 deletions(-) diff --git a/Authentication/AuthenticationTrustResolver.php b/Authentication/AuthenticationTrustResolver.php index 52967c6a..4f9d46f2 100644 --- a/Authentication/AuthenticationTrustResolver.php +++ b/Authentication/AuthenticationTrustResolver.php @@ -26,17 +26,11 @@ public function isAuthenticated(TokenInterface $token = null): bool return $token && $token->getUser(); } - /** - * {@inheritdoc} - */ public function isRememberMe(TokenInterface $token = null): bool { return $token && $token instanceof RememberMeToken; } - /** - * {@inheritdoc} - */ public function isFullFledged(TokenInterface $token = null): bool { return $this->isAuthenticated($token) && !$this->isRememberMe($token); diff --git a/Authentication/RememberMe/CacheTokenVerifier.php b/Authentication/RememberMe/CacheTokenVerifier.php index a9af5927..e4f1362a 100644 --- a/Authentication/RememberMe/CacheTokenVerifier.php +++ b/Authentication/RememberMe/CacheTokenVerifier.php @@ -35,9 +35,6 @@ public function __construct(CacheItemPoolInterface $cache, int $outdatedTokenTtl $this->cacheKeyPrefix = $cacheKeyPrefix; } - /** - * {@inheritdoc} - */ public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue): bool { if (hash_equals($token->getTokenValue(), $tokenValue)) { @@ -55,9 +52,6 @@ public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParamet return hash_equals($outdatedToken, $tokenValue); } - /** - * {@inheritdoc} - */ public function updateExistingToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed): void { // When a token gets updated, persist the outdated token for $outdatedTokenTtl seconds so we can diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index e6d52661..e47b29f5 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -22,9 +22,6 @@ class InMemoryTokenProvider implements TokenProviderInterface { private array $tokens = []; - /** - * {@inheritdoc} - */ public function loadTokenBySeries(string $series): PersistentTokenInterface { if (!isset($this->tokens[$series])) { @@ -34,9 +31,6 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface return $this->tokens[$series]; } - /** - * {@inheritdoc} - */ public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) { if (!isset($this->tokens[$series])) { @@ -53,17 +47,11 @@ public function updateToken(string $series, #[\SensitiveParameter] string $token $this->tokens[$series] = $token; } - /** - * {@inheritdoc} - */ public function deleteTokenBySeries(string $series) { unset($this->tokens[$series]); } - /** - * {@inheritdoc} - */ public function createNewToken(PersistentTokenInterface $token) { $this->tokens[$token->getSeries()] = $token; diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index 9f3f9e76..79e5fc23 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -46,9 +46,6 @@ public function __construct(string $class, string $userIdentifier, string $serie $this->lastUsed = $lastUsed; } - /** - * {@inheritdoc} - */ public function getClass(): string { return $this->class; @@ -59,25 +56,16 @@ public function getUserIdentifier(): string return $this->userIdentifier; } - /** - * {@inheritdoc} - */ public function getSeries(): string { return $this->series; } - /** - * {@inheritdoc} - */ public function getTokenValue(): string { return $this->tokenValue; } - /** - * {@inheritdoc} - */ public function getLastUsed(): \DateTime { return $this->lastUsed; diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 6b0a7378..63185eec 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -38,9 +38,6 @@ public function __construct(array $roles = []) } } - /** - * {@inheritdoc} - */ public function getRoleNames(): array { return $this->roleNames; @@ -51,25 +48,16 @@ public function getUserIdentifier(): string return $this->user ? $this->user->getUserIdentifier() : ''; } - /** - * {@inheritdoc} - */ public function getUser(): ?UserInterface { return $this->user; } - /** - * {@inheritdoc} - */ public function setUser(UserInterface $user) { $this->user = $user; } - /** - * {@inheritdoc} - */ public function eraseCredentials() { if ($this->getUser() instanceof UserInterface) { @@ -119,33 +107,21 @@ public function __unserialize(array $data): void $this->user = \is_string($user) ? new InMemoryUser($user, '', $this->roleNames, false) : $user; } - /** - * {@inheritdoc} - */ public function getAttributes(): array { return $this->attributes; } - /** - * {@inheritdoc} - */ public function setAttributes(array $attributes) { $this->attributes = $attributes; } - /** - * {@inheritdoc} - */ public function hasAttribute(string $name): bool { return \array_key_exists($name, $this->attributes); } - /** - * {@inheritdoc} - */ public function getAttribute(string $name): mixed { if (!\array_key_exists($name, $this->attributes)) { @@ -155,9 +131,6 @@ public function getAttribute(string $name): mixed return $this->attributes[$name]; } - /** - * {@inheritdoc} - */ public function setAttribute(string $name, mixed $value) { $this->attributes[$name] = $value; diff --git a/Authentication/Token/PreAuthenticatedToken.php b/Authentication/Token/PreAuthenticatedToken.php index da269b89..a216d4c1 100644 --- a/Authentication/Token/PreAuthenticatedToken.php +++ b/Authentication/Token/PreAuthenticatedToken.php @@ -42,17 +42,11 @@ public function getFirewallName(): string return $this->firewallName; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [null, $this->firewallName, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [, $this->firewallName, $parentData] = $data; diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index 147c1d68..cf99502e 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -56,17 +56,11 @@ public function getSecret(): string return $this->secret; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [$this->secret, $this->firewallName, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [$this->secret, $this->firewallName, $parentData] = $data; diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 72450dc9..73f536cb 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -27,9 +27,6 @@ class TokenStorage implements TokenStorageInterface, ResetInterface private ?TokenInterface $token = null; private ?\Closure $initializer = null; - /** - * {@inheritdoc} - */ public function getToken(): ?TokenInterface { if ($initializer = $this->initializer) { @@ -40,9 +37,6 @@ public function getToken(): ?TokenInterface return $this->token; } - /** - * {@inheritdoc} - */ public function setToken(TokenInterface $token = null) { if ($token) { diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index 46f6c96e..b6667127 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -34,9 +34,6 @@ public function __construct(TokenStorageInterface $storage, ContainerInterface $ $this->container = $container; } - /** - * {@inheritdoc} - */ public function getToken(): ?TokenInterface { if ($this->shouldTrackUsage()) { @@ -47,9 +44,6 @@ public function getToken(): ?TokenInterface return $this->storage->getToken(); } - /** - * {@inheritdoc} - */ public function setToken(TokenInterface $token = null): void { $this->storage->setToken($token); diff --git a/Authentication/Token/SwitchUserToken.php b/Authentication/Token/SwitchUserToken.php index feb8c297..ccb3c569 100644 --- a/Authentication/Token/SwitchUserToken.php +++ b/Authentication/Token/SwitchUserToken.php @@ -47,17 +47,11 @@ public function getOriginatedFromUri(): ?string return $this->originatedFromUri; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [$this->originalToken, $this->originatedFromUri, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { if (3 > \count($data)) { diff --git a/Authentication/Token/UsernamePasswordToken.php b/Authentication/Token/UsernamePasswordToken.php index cd25b2b6..74e24a21 100644 --- a/Authentication/Token/UsernamePasswordToken.php +++ b/Authentication/Token/UsernamePasswordToken.php @@ -39,17 +39,11 @@ public function getFirewallName(): string return $this->firewallName; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [null, $this->firewallName, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [, $this->firewallName, $parentData] = $data; diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index eccbec9b..ed585757 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -48,8 +48,6 @@ public function __construct(iterable $voters = [], AccessDecisionStrategyInterfa /** * @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array - * - * {@inheritdoc} */ public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false): bool { diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index b2b131d0..478ea01c 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -38,9 +38,6 @@ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionM $this->accessDecisionManager = $accessDecisionManager; } - /** - * {@inheritdoc} - */ final public function isGranted(mixed $attribute, mixed $subject = null): bool { $token = $this->tokenStorage->getToken(); diff --git a/Authorization/ExpressionLanguage.php b/Authorization/ExpressionLanguage.php index 6d9ad0ac..8f87767a 100644 --- a/Authorization/ExpressionLanguage.php +++ b/Authorization/ExpressionLanguage.php @@ -29,9 +29,6 @@ class_exists(ExpressionLanguageProvider::class); */ class ExpressionLanguage extends BaseExpressionLanguage { - /** - * {@inheritdoc} - */ public function __construct(CacheItemPoolInterface $cache = null, array $providers = []) { // prepend the default provider to let users override it easily diff --git a/Authorization/Strategy/AffirmativeStrategy.php b/Authorization/Strategy/AffirmativeStrategy.php index 3d6a5df3..ecd74b20 100644 --- a/Authorization/Strategy/AffirmativeStrategy.php +++ b/Authorization/Strategy/AffirmativeStrategy.php @@ -31,9 +31,6 @@ public function __construct(bool $allowIfAllAbstainDecisions = false) $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; } - /** - * {@inheritdoc} - */ public function decide(\Traversable $results): bool { $deny = 0; diff --git a/Authorization/Strategy/PriorityStrategy.php b/Authorization/Strategy/PriorityStrategy.php index d1942ede..9599950c 100644 --- a/Authorization/Strategy/PriorityStrategy.php +++ b/Authorization/Strategy/PriorityStrategy.php @@ -32,9 +32,6 @@ public function __construct(bool $allowIfAllAbstainDecisions = false) $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; } - /** - * {@inheritdoc} - */ public function decide(\Traversable $results): bool { foreach ($results as $result) { diff --git a/Authorization/Strategy/UnanimousStrategy.php b/Authorization/Strategy/UnanimousStrategy.php index 092fe3a0..1f3b85c5 100644 --- a/Authorization/Strategy/UnanimousStrategy.php +++ b/Authorization/Strategy/UnanimousStrategy.php @@ -31,9 +31,6 @@ public function __construct(bool $allowIfAllAbstainDecisions = false) $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; } - /** - * {@inheritdoc} - */ public function decide(\Traversable $results): bool { $grant = 0; diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 30d5de8f..c07297ea 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -45,9 +45,6 @@ public function __construct(AccessDecisionManagerInterface $manager) } } - /** - * {@inheritdoc} - */ public function decide(TokenInterface $token, array $attributes, mixed $object = null, bool $allowMultipleAttributes = false): bool { $currentDecisionLog = [ diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index bc72bb81..d7b2b224 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -40,9 +40,6 @@ public function __construct(AuthenticationTrustResolverInterface $authentication $this->authenticationTrustResolver = $authenticationTrustResolver; } - /** - * {@inheritdoc} - */ public function vote(TokenInterface $token, mixed $subject, array $attributes): int { if ($attributes === [self::PUBLIC_ACCESS]) { diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index 3f50dccc..87f191d8 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -49,9 +49,6 @@ public function supportsType(string $subjectType): bool return true; } - /** - * {@inheritdoc} - */ public function vote(TokenInterface $token, mixed $subject, array $attributes): int { $result = VoterInterface::ACCESS_ABSTAIN; diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php index 11c44138..18ef2413 100644 --- a/Authorization/Voter/RoleHierarchyVoter.php +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -31,9 +31,6 @@ public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefi parent::__construct($prefix); } - /** - * {@inheritdoc} - */ protected function extractRoles(TokenInterface $token) { return $this->roleHierarchy->getReachableRoleNames($token->getRoleNames()); diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index b751a31e..8e8d7181 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -27,9 +27,6 @@ public function __construct(string $prefix = 'ROLE_') $this->prefix = $prefix; } - /** - * {@inheritdoc} - */ public function vote(TokenInterface $token, mixed $subject, array $attributes): int { $result = VoterInterface::ACCESS_ABSTAIN; diff --git a/Authorization/Voter/Voter.php b/Authorization/Voter/Voter.php index 2382b72f..6ac75993 100644 --- a/Authorization/Voter/Voter.php +++ b/Authorization/Voter/Voter.php @@ -21,9 +21,6 @@ */ abstract class Voter implements VoterInterface, CacheableVoterInterface { - /** - * {@inheritdoc} - */ public function vote(TokenInterface $token, mixed $subject, array $attributes): int { // abstain vote by default in case none of the attributes are supported diff --git a/Exception/AccountExpiredException.php b/Exception/AccountExpiredException.php index b344d661..91ea122e 100644 --- a/Exception/AccountExpiredException.php +++ b/Exception/AccountExpiredException.php @@ -19,9 +19,6 @@ */ class AccountExpiredException extends AccountStatusException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Account has expired.'; diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index 76878f9f..58053905 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -37,17 +37,11 @@ public function setUser(UserInterface $user) $this->user = $user; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [$this->user, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [$this->user, $parentData] = $data; diff --git a/Exception/AuthenticationCredentialsNotFoundException.php b/Exception/AuthenticationCredentialsNotFoundException.php index 55e44424..fc28e4e5 100644 --- a/Exception/AuthenticationCredentialsNotFoundException.php +++ b/Exception/AuthenticationCredentialsNotFoundException.php @@ -20,9 +20,6 @@ */ class AuthenticationCredentialsNotFoundException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Authentication credentials could not be found.'; diff --git a/Exception/AuthenticationExpiredException.php b/Exception/AuthenticationExpiredException.php index a1a26fb1..1d04c5e8 100644 --- a/Exception/AuthenticationExpiredException.php +++ b/Exception/AuthenticationExpiredException.php @@ -21,9 +21,6 @@ */ class AuthenticationExpiredException extends AccountStatusException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Authentication expired because your account information has changed.'; diff --git a/Exception/AuthenticationServiceException.php b/Exception/AuthenticationServiceException.php index c57a969f..fa5042e1 100644 --- a/Exception/AuthenticationServiceException.php +++ b/Exception/AuthenticationServiceException.php @@ -19,9 +19,6 @@ */ class AuthenticationServiceException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Authentication request could not be processed due to a system problem.'; diff --git a/Exception/BadCredentialsException.php b/Exception/BadCredentialsException.php index e89598eb..6aeed7b0 100644 --- a/Exception/BadCredentialsException.php +++ b/Exception/BadCredentialsException.php @@ -19,9 +19,6 @@ */ class BadCredentialsException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Invalid credentials.'; diff --git a/Exception/CookieTheftException.php b/Exception/CookieTheftException.php index 4b8ebcf1..a32f30d5 100644 --- a/Exception/CookieTheftException.php +++ b/Exception/CookieTheftException.php @@ -20,9 +20,6 @@ */ class CookieTheftException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Cookie has already been used by someone else.'; diff --git a/Exception/CredentialsExpiredException.php b/Exception/CredentialsExpiredException.php index f21fcf8d..50183772 100644 --- a/Exception/CredentialsExpiredException.php +++ b/Exception/CredentialsExpiredException.php @@ -19,9 +19,6 @@ */ class CredentialsExpiredException extends AccountStatusException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Credentials have expired.'; diff --git a/Exception/CustomUserMessageAccountStatusException.php b/Exception/CustomUserMessageAccountStatusException.php index 569cfa0d..0c59a954 100644 --- a/Exception/CustomUserMessageAccountStatusException.php +++ b/Exception/CustomUserMessageAccountStatusException.php @@ -55,17 +55,11 @@ public function getMessageData(): array return $this->messageData; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [parent::__serialize(), $this->messageKey, $this->messageData]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [$parentData, $this->messageKey, $this->messageData] = $data; diff --git a/Exception/CustomUserMessageAuthenticationException.php b/Exception/CustomUserMessageAuthenticationException.php index 42f9c4df..b935df3a 100644 --- a/Exception/CustomUserMessageAuthenticationException.php +++ b/Exception/CustomUserMessageAuthenticationException.php @@ -54,17 +54,11 @@ public function getMessageData(): array return $this->messageData; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [parent::__serialize(), $this->messageKey, $this->messageData]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [$parentData, $this->messageKey, $this->messageData] = $data; diff --git a/Exception/DisabledException.php b/Exception/DisabledException.php index f598a16b..b82067cc 100644 --- a/Exception/DisabledException.php +++ b/Exception/DisabledException.php @@ -19,9 +19,6 @@ */ class DisabledException extends AccountStatusException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Account is disabled.'; diff --git a/Exception/InsufficientAuthenticationException.php b/Exception/InsufficientAuthenticationException.php index 9d26a304..0221dfd2 100644 --- a/Exception/InsufficientAuthenticationException.php +++ b/Exception/InsufficientAuthenticationException.php @@ -21,9 +21,6 @@ */ class InsufficientAuthenticationException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Not privileged to request the resource.'; diff --git a/Exception/InvalidCsrfTokenException.php b/Exception/InvalidCsrfTokenException.php index 7a90f0ef..2041cf6b 100644 --- a/Exception/InvalidCsrfTokenException.php +++ b/Exception/InvalidCsrfTokenException.php @@ -19,9 +19,6 @@ */ class InvalidCsrfTokenException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Invalid CSRF token.'; diff --git a/Exception/LockedException.php b/Exception/LockedException.php index 8943aee7..fb81cb05 100644 --- a/Exception/LockedException.php +++ b/Exception/LockedException.php @@ -19,9 +19,6 @@ */ class LockedException extends AccountStatusException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Account is locked.'; diff --git a/Exception/ProviderNotFoundException.php b/Exception/ProviderNotFoundException.php index 584a592e..e4daf4ef 100644 --- a/Exception/ProviderNotFoundException.php +++ b/Exception/ProviderNotFoundException.php @@ -20,9 +20,6 @@ */ class ProviderNotFoundException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'No authentication provider found to support the authentication token.'; diff --git a/Exception/SessionUnavailableException.php b/Exception/SessionUnavailableException.php index 1d55cb1e..eec069c5 100644 --- a/Exception/SessionUnavailableException.php +++ b/Exception/SessionUnavailableException.php @@ -25,9 +25,6 @@ */ class SessionUnavailableException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'No session available, it either timed out or cookies are not enabled.'; diff --git a/Exception/TokenNotFoundException.php b/Exception/TokenNotFoundException.php index 290b4f56..a18f0d08 100644 --- a/Exception/TokenNotFoundException.php +++ b/Exception/TokenNotFoundException.php @@ -19,9 +19,6 @@ */ class TokenNotFoundException extends AuthenticationException { - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'No token could be found.'; diff --git a/Exception/TooManyLoginAttemptsAuthenticationException.php b/Exception/TooManyLoginAttemptsAuthenticationException.php index ffd71149..eab1d506 100644 --- a/Exception/TooManyLoginAttemptsAuthenticationException.php +++ b/Exception/TooManyLoginAttemptsAuthenticationException.php @@ -26,9 +26,6 @@ public function __construct(int $threshold = null) $this->threshold = $threshold; } - /** - * {@inheritdoc} - */ public function getMessageData(): array { return [ @@ -37,25 +34,16 @@ public function getMessageData(): array ]; } - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Too many failed login attempts, please try again '.($this->threshold ? 'in %minutes% minute'.($this->threshold > 1 ? 's' : '').'.' : 'later.'); } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [$this->threshold, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [$this->threshold, $parentData] = $data; diff --git a/Exception/UserNotFoundException.php b/Exception/UserNotFoundException.php index 17f488b6..6cd9b712 100644 --- a/Exception/UserNotFoundException.php +++ b/Exception/UserNotFoundException.php @@ -21,9 +21,6 @@ class UserNotFoundException extends AuthenticationException { private ?string $identifier = null; - /** - * {@inheritdoc} - */ public function getMessageKey(): string { return 'Username could not be found.'; @@ -45,25 +42,16 @@ public function setUserIdentifier(string $identifier): void $this->identifier = $identifier; } - /** - * {@inheritdoc} - */ public function getMessageData(): array { return ['{{ username }}' => $this->identifier, '{{ user_identifier }}' => $this->identifier]; } - /** - * {@inheritdoc} - */ public function __serialize(): array { return [$this->identifier, parent::__serialize()]; } - /** - * {@inheritdoc} - */ public function __unserialize(array $data): void { [$this->identifier, $parentData] = $data; diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index f7b4b094..080647c8 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -32,9 +32,6 @@ public function __construct(array $hierarchy) $this->buildRoleMap(); } - /** - * {@inheritdoc} - */ public function getReachableRoleNames(array $roles): array { $reachableRoles = $roles; diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 29a6c4b6..47ebbc1a 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -69,9 +69,6 @@ public function loadUserByIdentifier(string $identifier): UserInterface throw $ex; } - /** - * {@inheritdoc} - */ public function refreshUser(UserInterface $user): UserInterface { $supportedUserFound = false; @@ -101,9 +98,6 @@ public function refreshUser(UserInterface $user): UserInterface } } - /** - * {@inheritdoc} - */ public function supportsClass(string $class): bool { foreach ($this->providers as $provider) { @@ -115,9 +109,6 @@ public function supportsClass(string $class): bool return false; } - /** - * {@inheritdoc} - */ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void { foreach ($this->providers as $provider) { diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index 1a506749..ca988417 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -43,17 +43,11 @@ public function __toString(): string return $this->getUserIdentifier(); } - /** - * {@inheritdoc} - */ public function getRoles(): array { return $this->roles; } - /** - * {@inheritdoc} - */ public function getPassword(): ?string { return $this->password; @@ -82,16 +76,10 @@ public function isEnabled(): bool return $this->enabled; } - /** - * {@inheritdoc} - */ public function eraseCredentials() { } - /** - * {@inheritdoc} - */ public function isEqualTo(UserInterface $user): bool { if (!$user instanceof self) { diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index b7e24813..dcf25c7c 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -69,9 +69,6 @@ public function loadUserByIdentifier(string $identifier): UserInterface return new InMemoryUser($user->getUserIdentifier(), $user->getPassword(), $user->getRoles(), $user->isEnabled()); } - /** - * {@inheritdoc} - */ public function refreshUser(UserInterface $user): UserInterface { if (!$user instanceof InMemoryUser) { @@ -84,9 +81,6 @@ public function refreshUser(UserInterface $user): UserInterface return new InMemoryUser($userIdentifier, $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); } - /** - * {@inheritdoc} - */ public function supportsClass(string $class): bool { return InMemoryUser::class == $class; diff --git a/User/MissingUserProvider.php b/User/MissingUserProvider.php index 02df0516..4d389ffc 100644 --- a/User/MissingUserProvider.php +++ b/User/MissingUserProvider.php @@ -29,9 +29,6 @@ public function __construct(string $firewall) throw new InvalidConfigurationException(sprintf('"%s" firewall requires a user provider but none was defined.', $firewall)); } - /** - * {@inheritdoc} - */ public function loadUserByUsername(string $username): UserInterface { throw new \BadMethodCallException(); @@ -42,17 +39,11 @@ public function loadUserByIdentifier(string $identifier): UserInterface throw new \BadMethodCallException(); } - /** - * {@inheritdoc} - */ public function refreshUser(UserInterface $user): UserInterface { throw new \BadMethodCallException(); } - /** - * {@inheritdoc} - */ public function supportsClass(string $class): bool { throw new \BadMethodCallException(); diff --git a/Validator/Constraints/UserPassword.php b/Validator/Constraints/UserPassword.php index ba37598e..f7be4bb6 100644 --- a/Validator/Constraints/UserPassword.php +++ b/Validator/Constraints/UserPassword.php @@ -31,9 +31,6 @@ public function __construct(array $options = null, string $message = null, strin $this->service = $service ?? $this->service; } - /** - * {@inheritdoc} - */ public function validatedBy(): string { return $this->service; diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 178cf771..56e7d35c 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -31,9 +31,6 @@ public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherF $this->hasherFactory = $hasherFactory; } - /** - * {@inheritdoc} - */ public function validate(mixed $password, Constraint $constraint) { if (!$constraint instanceof UserPassword) { From acae6347292c182daeea227c28b7dd353a61a70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20de=20Guillebon?= Date: Fri, 26 Aug 2022 16:19:22 +0200 Subject: [PATCH 049/138] Replace get_class() calls by ::class --- Authorization/AccessDecisionManager.php | 2 +- Signature/SignatureHasher.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index ed585757..025f6827 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -86,7 +86,7 @@ private function getVoters(array $attributes, $object = null): iterable $keyAttributes[] = \is_string($attribute) ? $attribute : null; } // use `get_class` to handle anonymous classes - $keyObject = \is_object($object) ? \get_class($object) : get_debug_type($object); + $keyObject = \is_object($object) ? $object::class : get_debug_type($object); foreach ($this->voters as $key => $voter) { if (!$voter instanceof CacheableVoterInterface) { yield $voter; diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index 29d508ce..39c02d56 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -88,7 +88,7 @@ public function computeSignatureHash(UserInterface $user, int $expires): string } if (!\is_scalar($value) && !$value instanceof \Stringable) { - throw new \InvalidArgumentException(sprintf('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned.', $property, \get_class($user), get_debug_type($value))); + throw new \InvalidArgumentException(sprintf('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned.', $property, $user::class, get_debug_type($value))); } $signatureFields[] = base64_encode($value); } From 130add8d160e3a0c5ab82e7f3e9dfe589f40e63b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 2 Sep 2022 16:55:07 +0200 Subject: [PATCH 050/138] CS fixes --- Authorization/AuthorizationChecker.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 478ea01c..3827f8b9 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -13,7 +13,6 @@ use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; /** * AuthorizationChecker is the main authorization point of the Security component. From 20917ce1f668737cbd1139ecbf307fa0f9c5e0bf Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 5 Nov 2020 00:19:24 +0100 Subject: [PATCH 051/138] Remove the default values from setters with a nullable parameter. --- Authentication/Token/Storage/TokenStorage.php | 4 ++++ .../Token/Storage/TokenStorageInterface.php | 2 +- .../Storage/UsageTrackingTokenStorage.php | 4 ++++ CHANGELOG.md | 4 +++- .../Token/Storage/TokenStorageTest.php | 21 +++++++++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 73f536cb..84d665a9 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -39,6 +39,10 @@ public function getToken(): ?TokenInterface public function setToken(TokenInterface $token = null) { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/security-core', '6.2', 'Calling "%s()" without any arguments is deprecated. Please explicitly pass null if you want to unset the token.', __METHOD__); + } + if ($token) { // ensure any initializer is called $this->getToken(); diff --git a/Authentication/Token/Storage/TokenStorageInterface.php b/Authentication/Token/Storage/TokenStorageInterface.php index 340f57d8..7d184d04 100644 --- a/Authentication/Token/Storage/TokenStorageInterface.php +++ b/Authentication/Token/Storage/TokenStorageInterface.php @@ -30,5 +30,5 @@ public function getToken(): ?TokenInterface; * * @param TokenInterface|null $token A TokenInterface token, or null if no further authentication information should be stored */ - public function setToken(TokenInterface $token = null); + public function setToken(?TokenInterface $token); } diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index b6667127..ef1a99a1 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -46,6 +46,10 @@ public function getToken(): ?TokenInterface public function setToken(TokenInterface $token = null): void { + if (1 > \func_num_args()) { + trigger_deprecation('symfony/security-core', '6.2', 'Calling "%s()" without any arguments is deprecated. Please explicitly pass null if you want to unset the token.', __METHOD__); + } + $this->storage->setToken($token); if ($token && $this->shouldTrackUsage()) { diff --git a/CHANGELOG.md b/CHANGELOG.md index 92f4ccca..cc5719dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ CHANGELOG 6.2 --- -* Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead + * Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead + * Change the signature of `TokenStorageInterface::setToken()` to `setToken(?TokenInterface $token)` + * Deprecate calling `TokenStorage::setToken()` or `UsageTrackingTokenStorage::setToken()` without arguments 6.0 --- diff --git a/Tests/Authentication/Token/Storage/TokenStorageTest.php b/Tests/Authentication/Token/Storage/TokenStorageTest.php index a08f90cd..4cddf50c 100644 --- a/Tests/Authentication/Token/Storage/TokenStorageTest.php +++ b/Tests/Authentication/Token/Storage/TokenStorageTest.php @@ -12,12 +12,31 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token\Storage; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\User\InMemoryUser; class TokenStorageTest extends TestCase { + use ExpectDeprecationTrait; + + /** + * @group legacy + */ + public function testGetSetTokenLegacy() + { + $tokenStorage = new TokenStorage(); + $token = new UsernamePasswordToken('username', 'password', 'provider'); + $tokenStorage->setToken($token); + $this->assertSame($token, $tokenStorage->getToken()); + + $this->expectDeprecation('Since symfony/security-core 6.2: Calling "Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage::setToken()" without any arguments is deprecated. Please explicitly pass null if you want to unset the token.'); + + $tokenStorage->setToken(); + $this->assertNull($tokenStorage->getToken()); + } + public function testGetSetToken() { $tokenStorage = new TokenStorage(); @@ -25,5 +44,7 @@ public function testGetSetToken() $token = new UsernamePasswordToken(new InMemoryUser('username', 'password'), 'provider'); $tokenStorage->setToken($token); $this->assertSame($token, $tokenStorage->getToken()); + $tokenStorage->setToken(null); + $this->assertNull($tokenStorage->getToken()); } } From 4acf4302a2300e90b60058ca34234b494fc92ab1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 Sep 2022 14:32:45 +0200 Subject: [PATCH 052/138] Remove all "nullable-by-default-value" setters --- Authentication/Token/Storage/TokenStorage.php | 2 +- Authentication/Token/Storage/UsageTrackingTokenStorage.php | 4 ---- CHANGELOG.md | 2 +- Tests/Authentication/Token/Storage/TokenStorageTest.php | 4 ++-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 84d665a9..e1f619a6 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -40,7 +40,7 @@ public function getToken(): ?TokenInterface public function setToken(TokenInterface $token = null) { if (1 > \func_num_args()) { - trigger_deprecation('symfony/security-core', '6.2', 'Calling "%s()" without any arguments is deprecated. Please explicitly pass null if you want to unset the token.', __METHOD__); + trigger_deprecation('symfony/security-core', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); } if ($token) { diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index ef1a99a1..b6667127 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -46,10 +46,6 @@ public function getToken(): ?TokenInterface public function setToken(TokenInterface $token = null): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/security-core', '6.2', 'Calling "%s()" without any arguments is deprecated. Please explicitly pass null if you want to unset the token.', __METHOD__); - } - $this->storage->setToken($token); if ($token && $this->shouldTrackUsage()) { diff --git a/CHANGELOG.md b/CHANGELOG.md index cc5719dc..82af50cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ CHANGELOG * Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead * Change the signature of `TokenStorageInterface::setToken()` to `setToken(?TokenInterface $token)` - * Deprecate calling `TokenStorage::setToken()` or `UsageTrackingTokenStorage::setToken()` without arguments + * Deprecate calling `TokenStorage::setToken()` without arguments 6.0 --- diff --git a/Tests/Authentication/Token/Storage/TokenStorageTest.php b/Tests/Authentication/Token/Storage/TokenStorageTest.php index 4cddf50c..5b260b50 100644 --- a/Tests/Authentication/Token/Storage/TokenStorageTest.php +++ b/Tests/Authentication/Token/Storage/TokenStorageTest.php @@ -27,11 +27,11 @@ class TokenStorageTest extends TestCase public function testGetSetTokenLegacy() { $tokenStorage = new TokenStorage(); - $token = new UsernamePasswordToken('username', 'password', 'provider'); + $token = new UsernamePasswordToken(new InMemoryUser('username', 'password'), 'provider'); $tokenStorage->setToken($token); $this->assertSame($token, $tokenStorage->getToken()); - $this->expectDeprecation('Since symfony/security-core 6.2: Calling "Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage::setToken()" without any arguments is deprecated. Please explicitly pass null if you want to unset the token.'); + $this->expectDeprecation('Since symfony/security-core 6.2: Calling "Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage::setToken()" without any arguments is deprecated, pass null explicitly instead.'); $tokenStorage->setToken(); $this->assertNull($tokenStorage->getToken()); From 8fb3946284947bd705b733d0d350adf14c910a1c Mon Sep 17 00:00:00 2001 From: Michael Babker Date: Fri, 15 Apr 2022 11:23:36 -0500 Subject: [PATCH 053/138] Add a ChainUserChecker to allow calling multiple user checkers for a firewall --- CHANGELOG.md | 1 + Tests/User/ChainUserCheckerTest.php | 64 +++++++++++++++++++++++++++++ User/ChainUserChecker.php | 36 ++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 Tests/User/ChainUserCheckerTest.php create mode 100644 User/ChainUserChecker.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 82af50cf..f276db02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead * Change the signature of `TokenStorageInterface::setToken()` to `setToken(?TokenInterface $token)` * Deprecate calling `TokenStorage::setToken()` without arguments + * Add a `ChainUserChecker` to allow calling multiple user checkers for a firewall 6.0 --- diff --git a/Tests/User/ChainUserCheckerTest.php b/Tests/User/ChainUserCheckerTest.php new file mode 100644 index 00000000..f55fa655 --- /dev/null +++ b/Tests/User/ChainUserCheckerTest.php @@ -0,0 +1,64 @@ + + * + * 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\User; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\User\ChainUserChecker; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; + +final class ChainUserCheckerTest extends TestCase +{ + public function testForwardsPreAuthToAllUserCheckers() + { + $user = $this->createMock(UserInterface::class); + + $checker1 = $this->createMock(UserCheckerInterface::class); + $checker1->expects($this->once()) + ->method('checkPreAuth') + ->with($user); + + $checker2 = $this->createMock(UserCheckerInterface::class); + $checker2->expects($this->once()) + ->method('checkPreAuth') + ->with($user); + + $checker3 = $this->createMock(UserCheckerInterface::class); + $checker3->expects($this->once()) + ->method('checkPreAuth') + ->with($user); + + (new ChainUserChecker([$checker1, $checker2, $checker3]))->checkPreAuth($user); + } + + public function testForwardsPostAuthToAllUserCheckers() + { + $user = $this->createMock(UserInterface::class); + + $checker1 = $this->createMock(UserCheckerInterface::class); + $checker1->expects($this->once()) + ->method('checkPostAuth') + ->with($user); + + $checker2 = $this->createMock(UserCheckerInterface::class); + $checker2->expects($this->once()) + ->method('checkPostAuth') + ->with($user); + + $checker3 = $this->createMock(UserCheckerInterface::class); + $checker3->expects($this->once()) + ->method('checkPostAuth') + ->with($user); + + (new ChainUserChecker([$checker1, $checker2, $checker3]))->checkPostAuth($user); + } +} diff --git a/User/ChainUserChecker.php b/User/ChainUserChecker.php new file mode 100644 index 00000000..f889d35d --- /dev/null +++ b/User/ChainUserChecker.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\User; + +final class ChainUserChecker implements UserCheckerInterface +{ + /** + * @param iterable $checkers + */ + public function __construct(private readonly iterable $checkers) + { + } + + public function checkPreAuth(UserInterface $user): void + { + foreach ($this->checkers as $checker) { + $checker->checkPreAuth($user); + } + } + + public function checkPostAuth(UserInterface $user): void + { + foreach ($this->checkers as $checker) { + $checker->checkPostAuth($user); + } + } +} From 449814ef6681d53b5767fcc273b0cbb3439cd3e5 Mon Sep 17 00:00:00 2001 From: Ibon Date: Fri, 30 Sep 2022 19:27:03 +0200 Subject: [PATCH 054/138] Norgweiang translations --- Resources/translations/security.nn.xlf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/translations/security.nn.xlf b/Resources/translations/security.nn.xlf index 89ca44fa..1c8e065d 100644 --- a/Resources/translations/security.nn.xlf +++ b/Resources/translations/security.nn.xlf @@ -70,6 +70,14 @@ Invalid or expired login link. Innloggingslenka er ugyldig eller utgjengen. + + Too many failed login attempts, please try again in %minutes% minute. + For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutt. + + + Too many failed login attempts, please try again in %minutes% minutes. + For mange mislykkede påloggingsforsøk, prøv igjen om %minutes% minutter. + From 758196d7ba1831b82e20d6b3eccd56e94b9ff80c Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 1 Oct 2022 18:34:57 +0200 Subject: [PATCH 055/138] Move the new Security helper to the root of SecurityBundle --- CHANGELOG.md | 2 +- Security.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f276db02..9d88a0ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ CHANGELOG 6.2 --- - * Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead + * Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security` instead * Change the signature of `TokenStorageInterface::setToken()` to `setToken(?TokenInterface $token)` * Deprecate calling `TokenStorage::setToken()` without arguments * Add a `ChainUserChecker` to allow calling multiple user checkers for a firewall diff --git a/Security.php b/Security.php index c57c506a..8ebb3f69 100644 --- a/Security.php +++ b/Security.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Security\Core; use Psr\Container\ContainerInterface; -use Symfony\Bundle\SecurityBundle\Security\Security as NewSecurityHelper; +use Symfony\Bundle\SecurityBundle\Security as NewSecurityHelper; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -20,26 +20,26 @@ /** * Helper class for commonly-needed security tasks. * - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security instead + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security instead */ class Security implements AuthorizationCheckerInterface { /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security::ACCESS_DENIED_ERROR instead + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead * * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:ACCESS_DENIED_ERROR. */ public const ACCESS_DENIED_ERROR = '_security.403_error'; /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security::AUTHENTICATION_ERROR instead + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead * * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:AUTHENTICATION_ERROR. */ public const AUTHENTICATION_ERROR = '_security.last_error'; /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security\Security::LAST_USERNAME instead + * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead * * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:LAST_USERNAME. */ From 6293478649d4eaddd9ceb812cfe641fbf4c28b7c Mon Sep 17 00:00:00 2001 From: sarbanha Date: Mon, 10 Oct 2022 11:06:25 +0400 Subject: [PATCH 056/138] [Security] TraceableAccessDecisionManager: fix inspecting voters of custom access decision managers --- Authorization/TraceableAccessDecisionManager.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index c07297ea..10299fe5 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -36,11 +36,13 @@ public function __construct(AccessDecisionManagerInterface $manager) { $this->manager = $manager; - if ($this->manager instanceof AccessDecisionManager) { - // The strategy and voters are stored in a private properties of the decorated service - $reflection = new \ReflectionProperty(AccessDecisionManager::class, 'strategy'); + // The strategy and voters are stored in a private properties of the decorated service + if (property_exists($manager, 'strategy')) { + $reflection = new \ReflectionProperty(\get_class($manager), 'strategy'); $this->strategy = $reflection->getValue($manager); - $reflection = new \ReflectionProperty(AccessDecisionManager::class, 'voters'); + } + if (property_exists($manager, 'voters')) { + $reflection = new \ReflectionProperty(\get_class($manager), 'voters'); $this->voters = $reflection->getValue($manager); } } From 86d3616f541654aaf96307d96a1763c09cb9d29d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 1 Nov 2022 22:49:27 +0100 Subject: [PATCH 057/138] Use ??= more --- Authorization/Voter/ExpressionVoter.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index 87f191d8..9369ef45 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -58,9 +58,7 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes): continue; } - if (null === $variables) { - $variables = $this->getVariables($token, $subject); - } + $variables ??= $this->getVariables($token, $subject); $result = VoterInterface::ACCESS_DENIED; if ($this->expressionLanguage->evaluate($attribute, $variables)) { From 93c8515bde76425914a5cb5e9d08c8426f7143a7 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Thu, 27 Oct 2022 11:20:07 +0200 Subject: [PATCH 058/138] [Validator] Add the type for ConstraintValidatorTestCase::createValidator --- Tests/Validator/Constraints/UserPasswordValidatorTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 3ca0e516..9ed78b81 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator; +use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; @@ -44,7 +45,7 @@ abstract class UserPasswordValidatorTest extends ConstraintValidatorTestCase */ protected $hasherFactory; - protected function createValidator() + protected function createValidator(): ConstraintValidatorInterface { return new UserPasswordValidator($this->tokenStorage, $this->hasherFactory); } From 33fc9461aefbddddf59fff4e377ad15be0505081 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 9 Nov 2022 14:47:29 +0100 Subject: [PATCH 059/138] [Validator] Improve return types in tests --- Tests/Validator/Constraints/UserPasswordValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 9ed78b81..d7c63792 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -45,7 +45,7 @@ abstract class UserPasswordValidatorTest extends ConstraintValidatorTestCase */ protected $hasherFactory; - protected function createValidator(): ConstraintValidatorInterface + protected function createValidator(): UserPasswordValidator { return new UserPasswordValidator($this->tokenStorage, $this->hasherFactory); } From aa8d792c3f4fd48f64e4be8f426a220c8fc8ac14 Mon Sep 17 00:00:00 2001 From: Shakhobiddin <38453814+shokhaa@users.noreply.github.com> Date: Fri, 18 Nov 2022 12:19:04 +0500 Subject: [PATCH 060/138] Update Security.php syntax error replace : to :: --- Security.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Security.php b/Security.php index 8ebb3f69..f1ebf822 100644 --- a/Security.php +++ b/Security.php @@ -27,21 +27,21 @@ class Security implements AuthorizationCheckerInterface /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:ACCESS_DENIED_ERROR. + * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::ACCESS_DENIED_ERROR. */ public const ACCESS_DENIED_ERROR = '_security.403_error'; /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:AUTHENTICATION_ERROR. + * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::AUTHENTICATION_ERROR. */ public const AUTHENTICATION_ERROR = '_security.last_error'; /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes:LAST_USERNAME. + * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::LAST_USERNAME. */ public const LAST_USERNAME = '_security.last_username'; From e234e3bfe96d8f05630018210061cc6ce3011c73 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Fri, 9 Dec 2022 00:19:01 +0800 Subject: [PATCH 061/138] Point `Security::*` constants to `SecurityRequestAttributes::*` ones --- Security.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Security.php b/Security.php index f1ebf822..97f1c8ce 100644 --- a/Security.php +++ b/Security.php @@ -26,22 +26,16 @@ class Security implements AuthorizationCheckerInterface { /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::ACCESS_DENIED_ERROR. */ public const ACCESS_DENIED_ERROR = '_security.403_error'; /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::AUTHENTICATION_ERROR. */ public const AUTHENTICATION_ERROR = '_security.last_error'; /** * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead - * - * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::LAST_USERNAME. */ public const LAST_USERNAME = '_security.last_username'; From 544a1f6428490bb2ed71559a6ae5d2939346d291 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 19 Nov 2022 17:15:51 +0100 Subject: [PATCH 062/138] Minor (comment only), part 2 Second attempt of https://github.com/symfony/symfony/pull/48242 Even if you didn't like "visual", you still can't say "its" to a user ;-) --- User/UserInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/User/UserInterface.php b/User/UserInterface.php index c9b0930b..cace8f6a 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -55,7 +55,7 @@ public function getRoles(): array; public function eraseCredentials(); /** - * Returns the identifier for this user (e.g. its username or email address). + * Returns the identifier for this user (e.g. username or email address). */ public function getUserIdentifier(): string; } From ffd1e14269208270ce86f289d850ccc58235c434 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 21 Dec 2022 19:25:36 +0100 Subject: [PATCH 063/138] Add generics to PasswordUpgraderInterface --- User/PasswordUpgraderInterface.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/User/PasswordUpgraderInterface.php b/User/PasswordUpgraderInterface.php index fd21f14a..6bf18c7f 100644 --- a/User/PasswordUpgraderInterface.php +++ b/User/PasswordUpgraderInterface.php @@ -13,6 +13,8 @@ /** * @author Nicolas Grekas + * + * @template TUser of PasswordAuthenticatedUserInterface */ interface PasswordUpgraderInterface { @@ -22,6 +24,8 @@ interface PasswordUpgraderInterface * This method should persist the new password in the user storage and update the $user object accordingly. * Because you don't want your users not being able to log in, this method should be opportunistic: * it's fine if it does nothing or if it fails without throwing any exception. + * + * @param TUser $user */ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void; } From 79419c487fc5a601876fd9ffcbf000828b5471ae Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 28 Dec 2022 15:47:09 +0100 Subject: [PATCH 064/138] Drop v1 contracts packages everywhere --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 7e5f8679..5f933534 100644 --- a/composer.json +++ b/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^1.1|^2|^3", - "symfony/service-contracts": "^1.1.6|^2|^3", + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", "symfony/password-hasher": "^5.4|^6.0" }, "require-dev": { From 8ea8e27a31d994e14a4a1af773e5388297177594 Mon Sep 17 00:00:00 2001 From: tigitz Date: Sun, 1 Jan 2023 19:45:34 +0100 Subject: [PATCH 065/138] Leverage arrow function syntax for closure --- Authorization/ExpressionLanguageProvider.php | 24 ++++--------------- .../Storage/UsageTrackingTokenStorageTest.php | 4 +--- Tests/Resources/TranslationFilesTest.php | 2 +- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/Authorization/ExpressionLanguageProvider.php b/Authorization/ExpressionLanguageProvider.php index 755efd65..d3e2dac0 100644 --- a/Authorization/ExpressionLanguageProvider.php +++ b/Authorization/ExpressionLanguageProvider.php @@ -24,29 +24,13 @@ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface public function getFunctions(): array { return [ - new ExpressionFunction('is_authenticated', function () { - return '$auth_checker->isGranted("IS_AUTHENTICATED")'; - }, function (array $variables) { - return $variables['auth_checker']->isGranted('IS_AUTHENTICATED'); - }), + new ExpressionFunction('is_authenticated', fn () => '$auth_checker->isGranted("IS_AUTHENTICATED")', fn (array $variables) => $variables['auth_checker']->isGranted('IS_AUTHENTICATED')), - new ExpressionFunction('is_fully_authenticated', function () { - return '$token && $auth_checker->isGranted("IS_AUTHENTICATED_FULLY")'; - }, function (array $variables) { - return $variables['token'] && $variables['auth_checker']->isGranted('IS_AUTHENTICATED_FULLY'); - }), + new ExpressionFunction('is_fully_authenticated', fn () => '$token && $auth_checker->isGranted("IS_AUTHENTICATED_FULLY")', fn (array $variables) => $variables['token'] && $variables['auth_checker']->isGranted('IS_AUTHENTICATED_FULLY')), - new ExpressionFunction('is_granted', function ($attributes, $object = 'null') { - return sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object); - }, function (array $variables, $attributes, $object = null) { - return $variables['auth_checker']->isGranted($attributes, $object); - }), + new ExpressionFunction('is_granted', fn ($attributes, $object = 'null') => sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object), fn (array $variables, $attributes, $object = null) => $variables['auth_checker']->isGranted($attributes, $object)), - new ExpressionFunction('is_remember_me', function () { - return '$token && $auth_checker->isGranted("IS_REMEMBERED")'; - }, function (array $variables) { - return $variables['token'] && $variables['auth_checker']->isGranted('IS_REMEMBERED'); - }), + new ExpressionFunction('is_remember_me', fn () => '$token && $auth_checker->isGranted("IS_REMEMBERED")', fn (array $variables) => $variables['token'] && $variables['auth_checker']->isGranted('IS_REMEMBERED')), ]; } } diff --git a/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php b/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php index f8f11d13..5671e650 100644 --- a/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php +++ b/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php @@ -68,9 +68,7 @@ public function testGetSetToken() public function testWithoutMainRequest() { - $locator = new class(['request_stack' => function () { - return new RequestStack(); - }]) implements ContainerInterface { + $locator = new class(['request_stack' => fn () => new RequestStack()]) implements ContainerInterface { use ServiceLocatorTrait; }; $tokenStorage = new TokenStorage(); diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index cd96e7e6..5b54f1eb 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -45,7 +45,7 @@ public function testTranslationFileIsValidWithoutEntityLoader($filePath) public function provideTranslationFiles() { return array_map( - function ($filePath) { return (array) $filePath; }, + fn ($filePath) => (array) $filePath, glob(\dirname(__DIR__, 2).'/Resources/translations/*.xlf') ); } From b2f5f5d2e06f4e6ead7fae647472110f64ebac78 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 20 Jan 2023 19:25:26 +0100 Subject: [PATCH 066/138] [Security] Add SymfonyCasts as a backer to the READMEs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00e74cba..f930a12b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 6.1 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.2 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video From cfa3fb0c372ba5f066a9ffaadbca5d89784a13e6 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 19 Jan 2023 13:18:20 +0100 Subject: [PATCH 067/138] Introduce stub for Voter --- Authorization/Voter/Voter.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Authorization/Voter/Voter.php b/Authorization/Voter/Voter.php index 6ac75993..1f76a42e 100644 --- a/Authorization/Voter/Voter.php +++ b/Authorization/Voter/Voter.php @@ -18,6 +18,9 @@ * * @author Roman Marintšenko * @author Grégoire Pineau + * + * @template TAttribute of string + * @template TSubject of mixed */ abstract class Voter implements VoterInterface, CacheableVoterInterface { @@ -74,13 +77,19 @@ public function supportsType(string $subjectType): bool /** * Determines if the attribute and subject are supported by this voter. * - * @param $subject The subject to secure, e.g. an object the user wants to access or any other PHP type + * @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type + * + * @psalm-assert-if-true TSubject $subject + * @psalm-assert-if-true TAttribute $attribute */ abstract protected function supports(string $attribute, mixed $subject): bool; /** * Perform a single access check operation on a given attribute, subject and token. * It is safe to assume that $attribute and $subject already passed the "supports()" method check. + * + * @param TAttribute $attribute + * @param TSubject $subject */ abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool; } From 2621e43fb9af477e4c1427a43a6f64f6bc0ad95c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 2 Feb 2023 09:11:04 +0100 Subject: [PATCH 068/138] [Security] Return 403 instead of 500 when no firewall is defined --- Exception/AccessDeniedException.php | 3 +++ Exception/AuthenticationException.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index 79fb6eb6..12614904 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -11,11 +11,14 @@ namespace Symfony\Component\Security\Core\Exception; +use Symfony\Component\HttpKernel\Attribute\WithHttpStatus; + /** * AccessDeniedException is thrown when the account has not the required role. * * @author Fabien Potencier */ +#[WithHttpStatus(403)] class AccessDeniedException extends RuntimeException { private array $attributes = []; diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index 298bc78c..69ac3d6e 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Exception; +use Symfony\Component\HttpKernel\Attribute\WithHttpStatus; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** @@ -19,6 +20,7 @@ * @author Fabien Potencier * @author Alexander */ +#[WithHttpStatus(401)] class AuthenticationException extends RuntimeException { /** @internal */ From 4756d09b12a9950ce5f6adbbd13fa36ebe70ee33 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 6 Feb 2023 06:06:48 +0100 Subject: [PATCH 069/138] [PHPUnit 10] Use `TestCase` suffix for abstract tests in `/Tests/` --- ...swordValidatorTest.php => UserPasswordValidatorTestCase.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Tests/Validator/Constraints/{UserPasswordValidatorTest.php => UserPasswordValidatorTestCase.php} (98%) diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php similarity index 98% rename from Tests/Validator/Constraints/UserPasswordValidatorTest.php rename to Tests/Validator/Constraints/UserPasswordValidatorTestCase.php index d7c63792..fa2e4f9d 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php @@ -25,7 +25,7 @@ /** * @author Bernhard Schussek */ -abstract class UserPasswordValidatorTest extends ConstraintValidatorTestCase +abstract class UserPasswordValidatorTestCase extends ConstraintValidatorTestCase { private const PASSWORD = 's3Cr3t'; private const SALT = '^S4lt$'; From 24381338a08fdd1313445a46db45413410467527 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Mon, 13 Feb 2023 00:00:11 +0100 Subject: [PATCH 070/138] Add missing PHPdoc return types --- Authorization/Voter/RoleHierarchyVoter.php | 3 +++ Authorization/Voter/RoleVoter.php | 3 +++ Event/AuthenticationEvent.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php index 18ef2413..c8db1485 100644 --- a/Authorization/Voter/RoleHierarchyVoter.php +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -31,6 +31,9 @@ public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefi parent::__construct($prefix); } + /** + * @return array + */ protected function extractRoles(TokenInterface $token) { return $this->roleHierarchy->getReachableRoleNames($token->getRoleNames()); diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index 8e8d7181..70dddcff 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -58,6 +58,9 @@ public function supportsType(string $subjectType): bool return true; } + /** + * @return array + */ protected function extractRoles(TokenInterface $token) { return $token->getRoleNames(); diff --git a/Event/AuthenticationEvent.php b/Event/AuthenticationEvent.php index 1b9269fe..054dd957 100644 --- a/Event/AuthenticationEvent.php +++ b/Event/AuthenticationEvent.php @@ -28,6 +28,9 @@ public function __construct(TokenInterface $token) $this->authenticationToken = $token; } + /** + * @return TokenInterface + */ public function getAuthenticationToken() { return $this->authenticationToken; From 4ba265f2e194cbe22f3f97792a6cf3fba9fbbabf Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Mon, 13 Feb 2023 00:00:27 +0100 Subject: [PATCH 071/138] Add PHP types to private methods and functions --- User/InMemoryUserProvider.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index dcf25c7c..b4ffa1dc 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -54,6 +54,10 @@ public function __construct(array $users = []) */ public function createUser(UserInterface $user) { + if (!$user instanceof InMemoryUser) { + trigger_deprecation('symfony/security-core', '6.3', 'Passing users that are not instance of "%s" to "%s" is deprecated, "%s" given.', InMemoryUser::class, __METHOD__, get_debug_type($user)); + } + $userIdentifier = strtolower($user->getUserIdentifier()); if (isset($this->users[$userIdentifier])) { throw new \LogicException('Another user with the same username already exists.'); @@ -90,8 +94,10 @@ public function supportsClass(string $class): bool * Returns the user by given username. * * @throws UserNotFoundException if user whose given username does not exist + * + * @return InMemoryUser change return type on 7.0 */ - private function getUser(string $username)/* : InMemoryUser */ + private function getUser(string $username): UserInterface { if (!isset($this->users[strtolower($username)])) { $ex = new UserNotFoundException(sprintf('Username "%s" does not exist.', $username)); From d0bbaa0cd5f1a3f1112b275a49ec13ac22fad9df Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 12 Feb 2023 23:57:18 +0100 Subject: [PATCH 072/138] Add void return types --- Authentication/RememberMe/InMemoryTokenProvider.php | 9 +++++++++ Authentication/Token/AbstractToken.php | 12 ++++++++++++ Authentication/Token/NullToken.php | 3 +++ Authentication/Token/Storage/TokenStorage.php | 6 ++++++ Authorization/TraceableAccessDecisionManager.php | 2 +- Exception/AccessDeniedException.php | 6 ++++++ Exception/AccountStatusException.php | 3 +++ Exception/AuthenticationException.php | 3 +++ .../CustomUserMessageAccountStatusException.php | 2 ++ .../CustomUserMessageAuthenticationException.php | 2 ++ Role/RoleHierarchy.php | 3 +++ User/InMemoryUser.php | 2 +- User/InMemoryUserChecker.php | 6 ++++++ Validator/Constraints/UserPasswordValidator.php | 3 +++ 14 files changed, 60 insertions(+), 2 deletions(-) diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index e47b29f5..341883f3 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -31,6 +31,9 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface return $this->tokens[$series]; } + /** + * @return void + */ public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) { if (!isset($this->tokens[$series])) { @@ -47,11 +50,17 @@ public function updateToken(string $series, #[\SensitiveParameter] string $token $this->tokens[$series] = $token; } + /** + * @return void + */ public function deleteTokenBySeries(string $series) { unset($this->tokens[$series]); } + /** + * @return void + */ public function createNewToken(PersistentTokenInterface $token) { $this->tokens[$token->getSeries()] = $token; diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 63185eec..4416cda6 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -53,11 +53,17 @@ public function getUser(): ?UserInterface return $this->user; } + /** + * @return void + */ public function setUser(UserInterface $user) { $this->user = $user; } + /** + * @return void + */ public function eraseCredentials() { if ($this->getUser() instanceof UserInterface) { @@ -112,6 +118,9 @@ public function getAttributes(): array return $this->attributes; } + /** + * @return void + */ public function setAttributes(array $attributes) { $this->attributes = $attributes; @@ -131,6 +140,9 @@ public function getAttribute(string $name): mixed return $this->attributes[$name]; } + /** + * @return void + */ public function setAttribute(string $name, mixed $value) { $this->attributes[$name] = $value; diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index 47e04eb4..3df95e52 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -43,6 +43,9 @@ public function getUserIdentifier(): string return ''; } + /** + * @return void + */ public function eraseCredentials() { } diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index e1f619a6..0ec6b1cf 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -37,6 +37,9 @@ public function getToken(): ?TokenInterface return $this->token; } + /** + * @return void + */ public function setToken(TokenInterface $token = null) { if (1 > \func_num_args()) { @@ -57,6 +60,9 @@ public function setInitializer(?callable $initializer): void $this->initializer = null === $initializer ? null : $initializer(...); } + /** + * @return void + */ public function reset() { $this->setToken(null); diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 10299fe5..48bf3bc8 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -72,7 +72,7 @@ public function decide(TokenInterface $token, array $attributes, mixed $object = * @param array $attributes attributes used for the vote * @param int $vote vote of the voter */ - public function addVoterVote(VoterInterface $voter, array $attributes, int $vote) + public function addVoterVote(VoterInterface $voter, array $attributes, int $vote): void { $currentLogIndex = \count($this->currentLog) - 1; $this->currentLog[$currentLogIndex]['voterDetails'][] = [ diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index 12614904..88804d65 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -34,6 +34,9 @@ public function getAttributes(): array return $this->attributes; } + /** + * @return void + */ public function setAttributes(array|string $attributes) { $this->attributes = (array) $attributes; @@ -44,6 +47,9 @@ public function getSubject(): mixed return $this->subject; } + /** + * @return void + */ public function setSubject(mixed $subject) { $this->subject = $subject; diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index 58053905..5b064929 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -32,6 +32,9 @@ public function getUser(): ?UserInterface return $this->user; } + /** + * @return void + */ public function setUser(UserInterface $user) { $this->user = $user; diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index 69ac3d6e..adec6f87 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -39,6 +39,9 @@ public function getToken(): ?TokenInterface return $this->token; } + /** + * @return void + */ public function setToken(TokenInterface $token) { $this->token = $token; diff --git a/Exception/CustomUserMessageAccountStatusException.php b/Exception/CustomUserMessageAccountStatusException.php index 0c59a954..829a22cd 100644 --- a/Exception/CustomUserMessageAccountStatusException.php +++ b/Exception/CustomUserMessageAccountStatusException.php @@ -38,6 +38,8 @@ public function __construct(string $message = '', array $messageData = [], int $ * * @param string $messageKey The message or message key * @param array $messageData Data to be passed into the translator + * + * @return void */ public function setSafeMessage(string $messageKey, array $messageData = []) { diff --git a/Exception/CustomUserMessageAuthenticationException.php b/Exception/CustomUserMessageAuthenticationException.php index b935df3a..40de92d2 100644 --- a/Exception/CustomUserMessageAuthenticationException.php +++ b/Exception/CustomUserMessageAuthenticationException.php @@ -37,6 +37,8 @@ public function __construct(string $message = '', array $messageData = [], int $ * * @param string $messageKey The message or message key * @param array $messageData Data to be passed into the translator + * + * @return void */ public function setSafeMessage(string $messageKey, array $messageData = []) { diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index 080647c8..da094d2b 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -49,6 +49,9 @@ public function getReachableRoleNames(array $roles): array return array_values(array_unique($reachableRoles)); } + /** + * @return void + */ protected function buildRoleMap() { $this->map = []; diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index ca988417..b4fc8f1d 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -76,7 +76,7 @@ public function isEnabled(): bool return $this->enabled; } - public function eraseCredentials() + public function eraseCredentials(): void { } diff --git a/User/InMemoryUserChecker.php b/User/InMemoryUserChecker.php index f738ee00..a493b00e 100644 --- a/User/InMemoryUserChecker.php +++ b/User/InMemoryUserChecker.php @@ -20,6 +20,9 @@ */ class InMemoryUserChecker implements UserCheckerInterface { + /** + * @return void + */ public function checkPreAuth(UserInterface $user) { if (!$user instanceof InMemoryUser) { @@ -33,6 +36,9 @@ public function checkPreAuth(UserInterface $user) } } + /** + * @return void + */ public function checkPostAuth(UserInterface $user) { } diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 56e7d35c..8fd2205a 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -31,6 +31,9 @@ public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherF $this->hasherFactory = $hasherFactory; } + /** + * @return void + */ public function validate(mixed $password, Constraint $constraint) { if (!$constraint instanceof UserPassword) { From 4e154e93dcd4e5b44d5e72c845307c6a50aa9ae7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 14 Feb 2023 09:53:37 +0100 Subject: [PATCH 073/138] Fix merge --- Tests/Authorization/ExpressionLanguageTest.php | 2 +- Tests/Authorization/Voter/AuthenticatedVoterTest.php | 4 ++-- Tests/SecurityTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Authorization/ExpressionLanguageTest.php b/Tests/Authorization/ExpressionLanguageTest.php index e1366732..8cc4810a 100644 --- a/Tests/Authorization/ExpressionLanguageTest.php +++ b/Tests/Authorization/ExpressionLanguageTest.php @@ -44,7 +44,7 @@ public function testIsAuthenticated($token, $expression, $result) $this->assertEquals($result, $expressionLanguage->evaluate($expression, $context)); } - public function provider() + public static function provider() { $roles = ['ROLE_USER', 'ROLE_ADMIN']; $user = new InMemoryUser('username', 'password', $roles); diff --git a/Tests/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Authorization/Voter/AuthenticatedVoterTest.php index 4d8c067c..88544c08 100644 --- a/Tests/Authorization/Voter/AuthenticatedVoterTest.php +++ b/Tests/Authorization/Voter/AuthenticatedVoterTest.php @@ -33,7 +33,7 @@ public function testVote($authenticated, $attributes, $expected) $this->assertSame($expected, $voter->vote($this->getToken($authenticated), null, $attributes)); } - public function getVoteTests() + public static function getVoteTests() { return [ ['fully', [], VoterInterface::ACCESS_ABSTAIN], @@ -63,7 +63,7 @@ public function testSupportsAttribute(string $attribute, bool $expected) $this->assertSame($expected, $voter->supportsAttribute($attribute)); } - public function provideAttributes() + public static function provideAttributes() { yield [AuthenticatedVoter::IS_AUTHENTICATED_FULLY, true]; yield [AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED, true]; diff --git a/Tests/SecurityTest.php b/Tests/SecurityTest.php index 63eca289..00436895 100644 --- a/Tests/SecurityTest.php +++ b/Tests/SecurityTest.php @@ -61,7 +61,7 @@ public function testGetUser($userInToken, $expectedUser) $this->assertSame($expectedUser, $security->getUser()); } - public function getUserTests() + public static function getUserTests() { yield [null, null]; From e2eff1819735362c97722e52916ccfebbc166817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Buliard?= Date: Thu, 9 Mar 2023 22:28:49 +0100 Subject: [PATCH 074/138] [Security] Add missing void PHPdoc return types --- Authentication/RememberMe/TokenProviderInterface.php | 6 ++++++ Authentication/Token/NullToken.php | 9 +++++++++ Authentication/Token/Storage/TokenStorageInterface.php | 2 ++ Authentication/Token/TokenInterface.php | 9 +++++++++ Tests/Authentication/AuthenticationTrustResolverTest.php | 8 ++++---- User/UserCheckerInterface.php | 4 ++++ User/UserInterface.php | 2 ++ 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Authentication/RememberMe/TokenProviderInterface.php b/Authentication/RememberMe/TokenProviderInterface.php index 9b32fdce..cf41e0c5 100644 --- a/Authentication/RememberMe/TokenProviderInterface.php +++ b/Authentication/RememberMe/TokenProviderInterface.php @@ -31,18 +31,24 @@ public function loadTokenBySeries(string $series); /** * Deletes all tokens belonging to series. + * + * @return void */ public function deleteTokenBySeries(string $series); /** * Updates the token according to this data. * + * @return void + * * @throws TokenNotFoundException if the token is not found */ public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed); /** * Creates a new token. + * + * @return void */ public function createNewToken(PersistentTokenInterface $token); } diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index 3df95e52..eabfe17b 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -33,6 +33,9 @@ public function getUser(): ?UserInterface return null; } + /** + * @return never + */ public function setUser(UserInterface $user) { throw new \BadMethodCallException('Cannot set user on a NullToken.'); @@ -55,6 +58,9 @@ public function getAttributes(): array return []; } + /** + * @return never + */ public function setAttributes(array $attributes) { throw new \BadMethodCallException('Cannot set attributes of NullToken.'); @@ -70,6 +76,9 @@ public function getAttribute(string $name): mixed return null; } + /** + * @return never + */ public function setAttribute(string $name, mixed $value) { throw new \BadMethodCallException('Cannot add attribute to NullToken.'); diff --git a/Authentication/Token/Storage/TokenStorageInterface.php b/Authentication/Token/Storage/TokenStorageInterface.php index 7d184d04..5fdfa4e9 100644 --- a/Authentication/Token/Storage/TokenStorageInterface.php +++ b/Authentication/Token/Storage/TokenStorageInterface.php @@ -29,6 +29,8 @@ public function getToken(): ?TokenInterface; * Sets the authentication token. * * @param TokenInterface|null $token A TokenInterface token, or null if no further authentication information should be stored + * + * @return void */ public function setToken(?TokenInterface $token); } diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index d9e1bb4c..d9b80ae1 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -50,12 +50,16 @@ public function getUser(): ?UserInterface; /** * Sets the authenticated user in the token. * + * @return void + * * @throws \InvalidArgumentException */ public function setUser(UserInterface $user); /** * Removes sensitive information from the token. + * + * @return void */ public function eraseCredentials(); @@ -63,6 +67,8 @@ public function getAttributes(): array; /** * @param array $attributes The token attributes + * + * @return void */ public function setAttributes(array $attributes); @@ -73,6 +79,9 @@ public function hasAttribute(string $name): bool; */ public function getAttribute(string $name): mixed; + /** + * @return void + */ public function setAttribute(string $name, mixed $value); /** diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index 8c0df274..02149ce3 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -111,7 +111,7 @@ public function getUser(): UserInterface return new InMemoryUser('wouter', '', ['ROLE_USER']); } - public function setUser($user) + public function setUser($user): void { } @@ -123,7 +123,7 @@ public function getUserIdentifier(): string { } - public function eraseCredentials() + public function eraseCredentials(): void { } @@ -131,7 +131,7 @@ public function getAttributes(): array { } - public function setAttributes(array $attributes) + public function setAttributes(array $attributes): void { } @@ -143,7 +143,7 @@ public function getAttribute(string $name): mixed { } - public function setAttribute(string $name, $value) + public function setAttribute(string $name, $value): void { } } diff --git a/User/UserCheckerInterface.php b/User/UserCheckerInterface.php index a7c5f179..91f21c71 100644 --- a/User/UserCheckerInterface.php +++ b/User/UserCheckerInterface.php @@ -26,6 +26,8 @@ interface UserCheckerInterface /** * Checks the user account before authentication. * + * @return void + * * @throws AccountStatusException */ public function checkPreAuth(UserInterface $user); @@ -33,6 +35,8 @@ public function checkPreAuth(UserInterface $user); /** * Checks the user account after authentication. * + * @return void + * * @throws AccountStatusException */ public function checkPostAuth(UserInterface $user); diff --git a/User/UserInterface.php b/User/UserInterface.php index cace8f6a..ef22340a 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -51,6 +51,8 @@ public function getRoles(): array; * * This is important if, at any given point, sensitive information like * the plain-text password is stored on this object. + * + * @return void */ public function eraseCredentials(); From 75990b73f78bd13b92172e67f0980053e94f3a98 Mon Sep 17 00:00:00 2001 From: Maximilian Zumbansen Date: Wed, 15 Mar 2023 14:12:04 +0100 Subject: [PATCH 075/138] [Security] Add argument `$exceptionCode` to `#[IsGranted]` --- Exception/AccessDeniedException.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index 88804d65..c95bae03 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -24,9 +24,9 @@ class AccessDeniedException extends RuntimeException private array $attributes = []; private mixed $subject = null; - public function __construct(string $message = 'Access Denied.', \Throwable $previous = null) + public function __construct(string $message = 'Access Denied.', \Throwable $previous = null, int $code = 403) { - parent::__construct($message, 403, $previous); + parent::__construct($message, $code, $previous); } public function getAttributes(): array From 7b10f10147563a892c9da3621a6d330a5ae07f14 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 3 Apr 2023 17:40:29 +0200 Subject: [PATCH 076/138] [Security] Add SymfonyCasts as a backer of version 6.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f930a12b..c7385af8 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 6.2 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.3 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video From f58f59fe8e3a7c8e44104aa4b0e8a7d456b647b2 Mon Sep 17 00:00:00 2001 From: Vincent Chalamon Date: Mon, 21 Nov 2022 18:27:04 +0100 Subject: [PATCH 077/138] [Security] Add OidcUserInfoTokenHandler and OidcUser --- CHANGELOG.md | 6 + Tests/User/OidcUserTest.php | 176 +++++++++++++++++ User/AttributesBasedUserProviderInterface.php | 32 +++ User/OidcUser.php | 184 ++++++++++++++++++ composer.json | 1 + 5 files changed, 399 insertions(+) create mode 100644 Tests/User/OidcUserTest.php create mode 100644 User/AttributesBasedUserProviderInterface.php create mode 100644 User/OidcUser.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba95c05..b489556c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +6.3 +--- + + * Add `AttributesBasedUserProviderInterface` to allow `$attributes` optional argument on `loadUserByIdentifier` + * Add `OidcUser` with OIDC support for `OidcUserInfoTokenHandler` + 6.2 --- diff --git a/Tests/User/OidcUserTest.php b/Tests/User/OidcUserTest.php new file mode 100644 index 00000000..79256283 --- /dev/null +++ b/Tests/User/OidcUserTest.php @@ -0,0 +1,176 @@ + + * + * 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\User; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\User\OidcUser; + +class OidcUserTest extends TestCase +{ + public function testCannotCreateUserWithoutSubProperty() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The "sub" claim cannot be empty.'); + + new OidcUser(); + } + + public function testCreateFullUserWithAdditionalClaimsUsingPositionalParameters() + { + $this->assertEquals(new OidcUser( + userIdentifier: 'john.doe', + roles: ['ROLE_USER', 'ROLE_ADMIN'], + sub: 'e21bf182-1538-406e-8ccb-e25a17aba39f', + name: 'John DOE', + givenName: 'John', + familyName: 'DOE', + middleName: 'Fitzgerald', + nickname: 'Johnny', + preferredUsername: 'john.doe', + profile: 'https://www.example.com/john-doe', + picture: 'https://www.example.com/pics/john-doe.jpg', + website: 'https://www.example.com', + email: 'john.doe@example.com', + emailVerified: true, + gender: 'male', + birthdate: '1980-05-15', + zoneinfo: 'Europe/Paris', + locale: 'fr-FR', + phoneNumber: '+33 (0) 6 12 34 56 78', + phoneNumberVerified: false, + address: [ + 'formatted' => '1 Rue des Moulins 75000 Paris - France', + 'street_address' => '1 Rue des Moulins', + 'locality' => 'Paris', + 'region' => 'Île-de-France', + 'postal_code' => '75000', + 'country' => 'France', + ], + updatedAt: (new \DateTimeImmutable())->setTimestamp(1669628917), + additionalClaims: [ + 'impersonator' => [ + 'username' => 'jane.doe@example.com', + ], + 'customId' => 12345, + ], + ), new OidcUser(...[ + 'userIdentifier' => 'john.doe', + 'roles' => ['ROLE_USER', 'ROLE_ADMIN'], + 'sub' => 'e21bf182-1538-406e-8ccb-e25a17aba39f', + 'name' => 'John DOE', + 'givenName' => 'John', + 'familyName' => 'DOE', + 'middleName' => 'Fitzgerald', + 'nickname' => 'Johnny', + 'preferredUsername' => 'john.doe', + 'profile' => 'https://www.example.com/john-doe', + 'picture' => 'https://www.example.com/pics/john-doe.jpg', + 'website' => 'https://www.example.com', + 'email' => 'john.doe@example.com', + 'emailVerified' => true, + 'gender' => 'male', + 'birthdate' => '1980-05-15', + 'zoneinfo' => 'Europe/Paris', + 'locale' => 'fr-FR', + 'phoneNumber' => '+33 (0) 6 12 34 56 78', + 'phoneNumberVerified' => false, + 'address' => [ + 'formatted' => '1 Rue des Moulins 75000 Paris - France', + 'street_address' => '1 Rue des Moulins', + 'locality' => 'Paris', + 'region' => 'Île-de-France', + 'postal_code' => '75000', + 'country' => 'France', + ], + 'updatedAt' => (new \DateTimeImmutable())->setTimestamp(1669628917), + 'impersonator' => [ + 'username' => 'jane.doe@example.com', + ], + 'customId' => 12345, + ])); + } + + public function testCreateFullUserWithAdditionalClaims() + { + $this->assertEquals(new OidcUser( + userIdentifier: 'john.doe', + roles: ['ROLE_USER', 'ROLE_ADMIN'], + sub: 'e21bf182-1538-406e-8ccb-e25a17aba39f', + name: 'John DOE', + givenName: 'John', + familyName: 'DOE', + middleName: 'Fitzgerald', + nickname: 'Johnny', + preferredUsername: 'john.doe', + profile: 'https://www.example.com/john-doe', + picture: 'https://www.example.com/pics/john-doe.jpg', + website: 'https://www.example.com', + email: 'john.doe@example.com', + emailVerified: true, + gender: 'male', + birthdate: '1980-05-15', + zoneinfo: 'Europe/Paris', + locale: 'fr-FR', + phoneNumber: '+33 (0) 6 12 34 56 78', + phoneNumberVerified: false, + address: [ + 'formatted' => '1 Rue des Moulins 75000 Paris - France', + 'street_address' => '1 Rue des Moulins', + 'locality' => 'Paris', + 'region' => 'Île-de-France', + 'postal_code' => '75000', + 'country' => 'France', + ], + updatedAt: (new \DateTimeImmutable())->setTimestamp(1669628917), + additionalClaims: [ + [ + 'username' => 'jane.doe@example.com', + ], + 12345, + ], + ), new OidcUser( + 'john.doe', + ['ROLE_USER', 'ROLE_ADMIN'], + 'e21bf182-1538-406e-8ccb-e25a17aba39f', + 'John DOE', + 'John', + 'DOE', + 'Fitzgerald', + 'Johnny', + 'john.doe', + 'https://www.example.com/john-doe', + 'https://www.example.com/pics/john-doe.jpg', + 'https://www.example.com', + 'john.doe@example.com', + true, + 'male', + '1980-05-15', + 'Europe/Paris', + 'fr-FR', + '+33 (0) 6 12 34 56 78', + false, + [ + 'formatted' => '1 Rue des Moulins 75000 Paris - France', + 'street_address' => '1 Rue des Moulins', + 'locality' => 'Paris', + 'region' => 'Île-de-France', + 'postal_code' => '75000', + 'country' => 'France', + ], + (new \DateTimeImmutable())->setTimestamp(1669628917), + [ + 'username' => 'jane.doe@example.com', + ], + 12345 + )); + } +} diff --git a/User/AttributesBasedUserProviderInterface.php b/User/AttributesBasedUserProviderInterface.php new file mode 100644 index 00000000..10cbb434 --- /dev/null +++ b/User/AttributesBasedUserProviderInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\User; + +use Symfony\Component\Security\Core\Exception\UserNotFoundException; + +/** + * Overrides UserProviderInterface to add an "attributes" argument on loadUserByIdentifier. + * This is particularly useful with self-contained access tokens. + * + * @experimental + */ +interface AttributesBasedUserProviderInterface extends UserProviderInterface +{ + /** + * Loads the user for the given user identifier (e.g. username or email) and attributes. + * + * This method must throw UserNotFoundException if the user is not found. + * + * @throws UserNotFoundException + */ + public function loadUserByIdentifier(string $identifier, array $attributes = []): UserInterface; +} diff --git a/User/OidcUser.php b/User/OidcUser.php new file mode 100644 index 00000000..eea433b5 --- /dev/null +++ b/User/OidcUser.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\User; + +/** + * UserInterface implementation used by the access-token security workflow with an OIDC server. + * + * @experimental + */ +class OidcUser implements UserInterface +{ + private array $additionalClaims = []; + + public function __construct( + private ?string $userIdentifier = null, + private array $roles = ['ROLE_USER'], + + // Standard Claims (https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) + private ?string $sub = null, + private ?string $name = null, + private ?string $givenName = null, + private ?string $familyName = null, + private ?string $middleName = null, + private ?string $nickname = null, + private ?string $preferredUsername = null, + private ?string $profile = null, + private ?string $picture = null, + private ?string $website = null, + private ?string $email = null, + private ?bool $emailVerified = null, + private ?string $gender = null, + private ?string $birthdate = null, + private ?string $zoneinfo = null, + private ?string $locale = null, + private ?string $phoneNumber = null, + private ?bool $phoneNumberVerified = null, + private ?array $address = null, + private ?\DateTimeInterface $updatedAt = null, + + // Additional Claims (https://openid.net/specs/openid-connect-core-1_0.html#AdditionalClaims) + ...$additionalClaims + ) { + if (null === $sub || '' === $sub) { + throw new \InvalidArgumentException('The "sub" claim cannot be empty.'); + } + + $this->additionalClaims = $additionalClaims['additionalClaims'] ?? $additionalClaims; + } + + /** + * OIDC or OAuth specs don't have any "role" notion. + * + * If you want to implement "roles" from your OIDC server, + * send a "roles" constructor argument to this object + * (e.g.: using a custom UserProvider). + */ + public function getRoles(): array + { + return $this->roles; + } + + public function getUserIdentifier(): string + { + return (string) ($this->userIdentifier ?? $this->getSub()); + } + + public function eraseCredentials() + { + } + + public function getSub(): ?string + { + return $this->sub; + } + + public function getName(): ?string + { + return $this->name; + } + + public function getGivenName(): ?string + { + return $this->givenName; + } + + public function getFamilyName(): ?string + { + return $this->familyName; + } + + public function getMiddleName(): ?string + { + return $this->middleName; + } + + public function getNickname(): ?string + { + return $this->nickname; + } + + public function getPreferredUsername(): ?string + { + return $this->preferredUsername; + } + + public function getProfile(): ?string + { + return $this->profile; + } + + public function getPicture(): ?string + { + return $this->picture; + } + + public function getWebsite(): ?string + { + return $this->website; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function getEmailVerified(): ?bool + { + return $this->emailVerified; + } + + public function getGender(): ?string + { + return $this->gender; + } + + public function getBirthdate(): ?string + { + return $this->birthdate; + } + + public function getZoneinfo(): ?string + { + return $this->zoneinfo; + } + + public function getLocale(): ?string + { + return $this->locale; + } + + public function getPhoneNumber(): ?string + { + return $this->phoneNumber; + } + + public function getphoneNumberVerified(): ?bool + { + return $this->phoneNumberVerified; + } + + public function getAddress(): ?array + { + return $this->address; + } + + public function getUpdatedAt(): ?\DateTimeInterface + { + return $this->updatedAt; + } + + public function getAdditionalClaims(): array + { + return $this->additionalClaims; + } +} diff --git a/composer.json b/composer.json index 5f933534..8613e703 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "symfony/expression-language": "^5.4|^6.0", "symfony/http-foundation": "^5.4|^6.0", "symfony/ldap": "^5.4|^6.0", + "symfony/string": "^5.4|^6.0", "symfony/translation": "^5.4|^6.0", "symfony/validator": "^5.4|^6.0", "psr/log": "^1|^2|^3" From 9d855761e14527e9744d37af89900682473151a0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 14 Apr 2023 10:26:59 +0200 Subject: [PATCH 078/138] [Security] Fix OidcUser::eraseCredentials() signature --- User/OidcUser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/User/OidcUser.php b/User/OidcUser.php index eea433b5..c88d34b7 100644 --- a/User/OidcUser.php +++ b/User/OidcUser.php @@ -73,7 +73,7 @@ public function getUserIdentifier(): string return (string) ($this->userIdentifier ?? $this->getSub()); } - public function eraseCredentials() + public function eraseCredentials(): void { } From 4cd20fe5382bd0e3a7b685f98ade6fd9f9046d45 Mon Sep 17 00:00:00 2001 From: Oleh Korneliuk Date: Thu, 20 Apr 2023 12:33:20 +0300 Subject: [PATCH 079/138] [Security] Added error code to `UserPassword` constraint --- .../Constraints/UserPasswordValidatorTestCase.php | 2 ++ Validator/Constraints/UserPassword.php | 6 ++++++ Validator/Constraints/UserPasswordValidator.php | 8 ++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php index 3a810def..e3f71b70 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php @@ -87,6 +87,7 @@ public function testPasswordIsNotValid(UserPassword $constraint) $this->validator->validate('secret', $constraint); $this->buildViolation('myMessage') + ->setCode(UserPassword::INVALID_PASSWORD_ERROR) ->assertRaised(); } @@ -109,6 +110,7 @@ public function testEmptyPasswordsAreNotValid($password) $this->validator->validate($password, $constraint); $this->buildViolation('myMessage') + ->setCode(UserPassword::INVALID_PASSWORD_ERROR) ->assertRaised(); } diff --git a/Validator/Constraints/UserPassword.php b/Validator/Constraints/UserPassword.php index f7be4bb6..b1a340c8 100644 --- a/Validator/Constraints/UserPassword.php +++ b/Validator/Constraints/UserPassword.php @@ -20,6 +20,12 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class UserPassword extends Constraint { + public const INVALID_PASSWORD_ERROR = '2d2a8bb4-ddc8-45e4-9b0f-8670d3a3e290'; + + protected const ERROR_NAMES = [ + self::INVALID_PASSWORD_ERROR => 'INVALID_PASSWORD_ERROR', + ]; + public $message = 'This value should be the user\'s current password.'; public $service = 'security.validator.user_password'; diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 8fd2205a..41670b27 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -41,7 +41,9 @@ public function validate(mixed $password, Constraint $constraint) } if (null === $password || '' === $password) { - $this->context->addViolation($constraint->message); + $this->context->buildViolation($constraint->message) + ->setCode(UserPassword::INVALID_PASSWORD_ERROR) + ->addViolation(); return; } @@ -59,7 +61,9 @@ public function validate(mixed $password, Constraint $constraint) $hasher = $this->hasherFactory->getPasswordHasher($user); if (null === $user->getPassword() || !$hasher->verify($user->getPassword(), $password, $user instanceof LegacyPasswordAuthenticatedUserInterface ? $user->getSalt() : null)) { - $this->context->addViolation($constraint->message); + $this->context->buildViolation($constraint->message) + ->setCode(UserPassword::INVALID_PASSWORD_ERROR) + ->addViolation(); } } } From 4e67f0c02cface7fcd0b2d39879a07e2ed2a57f0 Mon Sep 17 00:00:00 2001 From: Artyum Petrov <17199757+artyuum@users.noreply.github.com> Date: Thu, 20 Apr 2023 19:24:19 +0400 Subject: [PATCH 080/138] Add "composer require..." in all exception messages when needed --- composer.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/composer.json b/composer.json index 8613e703..48dd96f9 100644 --- a/composer.json +++ b/composer.json @@ -41,14 +41,6 @@ "symfony/ldap": "<5.4", "symfony/validator": "<5.4" }, - "suggest": { - "psr/container-implementation": "To instantiate the Security class", - "symfony/event-dispatcher": "", - "symfony/http-foundation": "", - "symfony/validator": "For using the user password constraint", - "symfony/expression-language": "For using the expression voter", - "symfony/ldap": "For using LDAP integration" - }, "autoload": { "psr-4": { "Symfony\\Component\\Security\\Core\\": "" }, "exclude-from-classmap": [ From c2712ac1d8cd9cb1b63e745c1f24f6e722f3169c Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 22 Apr 2023 23:52:28 +0200 Subject: [PATCH 081/138] Add remaining missing return types to safe methods --- Authentication/Token/AbstractToken.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 4416cda6..10cb8f77 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -172,7 +172,7 @@ final public function serialize(): string /** * @internal */ - final public function unserialize(string $serialized) + final public function unserialize(string $serialized): void { $this->__unserialize(unserialize($serialized)); } From 9cb74232e978be1440d2bb7daf91eb40a9363890 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Fri, 28 Apr 2023 14:21:20 +0200 Subject: [PATCH 082/138] Add missing return types --- User/InMemoryUserProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index ed6c2d8b..e0aef90a 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -50,6 +50,8 @@ public function __construct(array $users = []) /** * Adds a new User to the provider. * + * @return void + * * @throws \LogicException */ public function createUser(UserInterface $user) From e2e75c40db284e85adcc1ee81bd99cf54ba1e9e0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:38:00 +0200 Subject: [PATCH 083/138] [6.4] Allow 7.0 deps --- composer.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 48dd96f9..ba1c62fa 100644 --- a/composer.json +++ b/composer.json @@ -19,19 +19,19 @@ "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/password-hasher": "^5.4|^6.0" + "symfony/password-hasher": "^5.4|^6.0|^7.0" }, "require-dev": { "psr/container": "^1.1|^2.0", "psr/cache": "^1.0|^2.0|^3.0", - "symfony/cache": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/ldap": "^5.4|^6.0", - "symfony/string": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/validator": "^5.4|^6.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/ldap": "^5.4|^6.0|^7.0", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/validator": "^5.4|^6.0|^7.0", "psr/log": "^1|^2|^3" }, "conflict": { From 78478b0c563999eb67fdcfc7ea0462c8c452dbde Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:24:39 +0200 Subject: [PATCH 084/138] [7.0] Bump to PHP 8.2 minimum --- composer.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index ba1c62fa..0bc2c140 100644 --- a/composer.json +++ b/composer.json @@ -16,30 +16,30 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/password-hasher": "^5.4|^6.0|^7.0" + "symfony/password-hasher": "^6.4|^7.0" }, "require-dev": { "psr/container": "^1.1|^2.0", "psr/cache": "^1.0|^2.0|^3.0", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/ldap": "^5.4|^6.0|^7.0", - "symfony/string": "^5.4|^6.0|^7.0", - "symfony/translation": "^5.4|^6.0|^7.0", - "symfony/validator": "^5.4|^6.0|^7.0", + "symfony/cache": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/ldap": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", "psr/log": "^1|^2|^3" }, "conflict": { - "symfony/event-dispatcher": "<5.4", - "symfony/http-foundation": "<5.4", - "symfony/security-guard": "<5.4", - "symfony/ldap": "<5.4", - "symfony/validator": "<5.4" + "symfony/event-dispatcher": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/security-guard": "<6.4", + "symfony/ldap": "<6.4", + "symfony/validator": "<6.4" }, "autoload": { "psr-4": { "Symfony\\Component\\Security\\Core\\": "" }, From bf58846b673cbfeb597360c8c8fb6096afd0f214 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 Jun 2023 10:51:15 +0200 Subject: [PATCH 085/138] [7.0] Fix various `@psalm-return` annotations --- Authorization/Voter/VoterInterface.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Authorization/Voter/VoterInterface.php b/Authorization/Voter/VoterInterface.php index 8eea57e7..5255c88e 100644 --- a/Authorization/Voter/VoterInterface.php +++ b/Authorization/Voter/VoterInterface.php @@ -33,9 +33,7 @@ interface VoterInterface * @param mixed $subject The subject to secure * @param array $attributes An array of attributes associated with the method being invoked * - * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED - * - * @psalm-return self::ACCESS_* must be transformed into @return on Symfony 7 + * @return self::ACCESS_* */ - public function vote(TokenInterface $token, mixed $subject, array $attributes); + public function vote(TokenInterface $token, mixed $subject, array $attributes): int; } From b2105347fb9e355c5e039c8c708cb95bf66a14ce Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Jul 2023 10:16:29 +0200 Subject: [PATCH 086/138] [SecurityBundle] Deprecate `Security::*` consts and other cleanups --- Security.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Security.php b/Security.php index 97f1c8ce..bb2576a7 100644 --- a/Security.php +++ b/Security.php @@ -24,19 +24,8 @@ */ class Security implements AuthorizationCheckerInterface { - /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead - */ public const ACCESS_DENIED_ERROR = '_security.403_error'; - - /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead - */ public const AUTHENTICATION_ERROR = '_security.last_error'; - - /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead - */ public const LAST_USERNAME = '_security.last_username'; /** From 3c7de5601a92acae8f4279d8bc9fdbd1f8fd3091 Mon Sep 17 00:00:00 2001 From: Frank Fiebig Date: Tue, 4 Jul 2023 11:21:50 +0200 Subject: [PATCH 087/138] [Lock] 7.0 remove deprecations in Lock Component --- Authentication/Token/Storage/TokenStorage.php | 6 +- Authorization/AuthorizationChecker.php | 15 +-- Security.php | 11 --- .../Token/Storage/TokenStorageTest.php | 19 ---- Tests/SecurityTest.php | 98 ------------------- User/ChainUserProvider.php | 8 -- User/InMemoryUserProvider.php | 10 +- 7 files changed, 8 insertions(+), 159 deletions(-) delete mode 100644 Tests/SecurityTest.php diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 0ec6b1cf..8acc31bc 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -40,12 +40,8 @@ public function getToken(): ?TokenInterface /** * @return void */ - public function setToken(TokenInterface $token = null) + public function setToken(?TokenInterface $token) { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/security-core', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } - if ($token) { // ensure any initializer is called $this->getToken(); diff --git a/Authorization/AuthorizationChecker.php b/Authorization/AuthorizationChecker.php index 3827f8b9..c748697c 100644 --- a/Authorization/AuthorizationChecker.php +++ b/Authorization/AuthorizationChecker.php @@ -24,17 +24,10 @@ */ class AuthorizationChecker implements AuthorizationCheckerInterface { - private TokenStorageInterface $tokenStorage; - private AccessDecisionManagerInterface $accessDecisionManager; - - public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, bool $exceptionOnNoToken = false) - { - if ($exceptionOnNoToken) { - throw new \LogicException(sprintf('Argument $exceptionOnNoToken of "%s()" must be set to "false".', __METHOD__)); - } - - $this->tokenStorage = $tokenStorage; - $this->accessDecisionManager = $accessDecisionManager; + public function __construct( + private TokenStorageInterface $tokenStorage, + private AccessDecisionManagerInterface $accessDecisionManager, + ) { } final public function isGranted(mixed $attribute, mixed $subject = null): bool diff --git a/Security.php b/Security.php index 97f1c8ce..bb2576a7 100644 --- a/Security.php +++ b/Security.php @@ -24,19 +24,8 @@ */ class Security implements AuthorizationCheckerInterface { - /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead - */ public const ACCESS_DENIED_ERROR = '_security.403_error'; - - /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead - */ public const AUTHENTICATION_ERROR = '_security.last_error'; - - /** - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead - */ public const LAST_USERNAME = '_security.last_username'; /** diff --git a/Tests/Authentication/Token/Storage/TokenStorageTest.php b/Tests/Authentication/Token/Storage/TokenStorageTest.php index 5b260b50..26d20ae4 100644 --- a/Tests/Authentication/Token/Storage/TokenStorageTest.php +++ b/Tests/Authentication/Token/Storage/TokenStorageTest.php @@ -12,31 +12,12 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Token\Storage; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\User\InMemoryUser; class TokenStorageTest extends TestCase { - use ExpectDeprecationTrait; - - /** - * @group legacy - */ - public function testGetSetTokenLegacy() - { - $tokenStorage = new TokenStorage(); - $token = new UsernamePasswordToken(new InMemoryUser('username', 'password'), 'provider'); - $tokenStorage->setToken($token); - $this->assertSame($token, $tokenStorage->getToken()); - - $this->expectDeprecation('Since symfony/security-core 6.2: Calling "Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage::setToken()" without any arguments is deprecated, pass null explicitly instead.'); - - $tokenStorage->setToken(); - $this->assertNull($tokenStorage->getToken()); - } - public function testGetSetToken() { $tokenStorage = new TokenStorage(); diff --git a/Tests/SecurityTest.php b/Tests/SecurityTest.php deleted file mode 100644 index 00436895..00000000 --- a/Tests/SecurityTest.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * 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; - -use PHPUnit\Framework\TestCase; -use Psr\Container\ContainerInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Component\Security\Core\Security; -use Symfony\Component\Security\Core\User\InMemoryUser; - -/** - * @group legacy - */ -class SecurityTest extends TestCase -{ - public function testGetToken() - { - $token = new UsernamePasswordToken(new InMemoryUser('foo', 'bar'), 'provider'); - $tokenStorage = $this->createMock(TokenStorageInterface::class); - - $tokenStorage->expects($this->once()) - ->method('getToken') - ->willReturn($token); - - $container = $this->createContainer('security.token_storage', $tokenStorage); - - $security = new Security($container); - $this->assertSame($token, $security->getToken()); - } - - /** - * @dataProvider getUserTests - */ - public function testGetUser($userInToken, $expectedUser) - { - $token = $this->createMock(TokenInterface::class); - $token->expects($this->any()) - ->method('getUser') - ->willReturn($userInToken); - $tokenStorage = $this->createMock(TokenStorageInterface::class); - - $tokenStorage->expects($this->once()) - ->method('getToken') - ->willReturn($token); - - $container = $this->createContainer('security.token_storage', $tokenStorage); - - $security = new Security($container); - $this->assertSame($expectedUser, $security->getUser()); - } - - public static function getUserTests() - { - yield [null, null]; - - $user = new InMemoryUser('nice_user', 'foo'); - yield [$user, $user]; - } - - public function testIsGranted() - { - $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); - - $authorizationChecker->expects($this->once()) - ->method('isGranted') - ->with('SOME_ATTRIBUTE', 'SOME_SUBJECT') - ->willReturn(true); - - $container = $this->createContainer('security.authorization_checker', $authorizationChecker); - - $security = new Security($container); - $this->assertTrue($security->isGranted('SOME_ATTRIBUTE', 'SOME_SUBJECT')); - } - - private function createContainer($serviceId, $serviceObject) - { - $container = $this->createMock(ContainerInterface::class); - - $container->expects($this->atLeastOnce()) - ->method('get') - ->with($serviceId) - ->willReturn($serviceObject); - - return $container; - } -} diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 47ebbc1a..045697fb 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -46,14 +46,6 @@ public function getProviders(): array return $this->providers; } - /** - * @internal for compatibility with Symfony 5.4 - */ - public function loadUserByUsername(string $username): UserInterface - { - return $this->loadUserByIdentifier($username); - } - public function loadUserByIdentifier(string $identifier): UserInterface { foreach ($this->providers as $provider) { diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index e0aef90a..13441bc7 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -51,13 +51,11 @@ public function __construct(array $users = []) * Adds a new User to the provider. * * @return void - * - * @throws \LogicException */ public function createUser(UserInterface $user) { if (!$user instanceof InMemoryUser) { - trigger_deprecation('symfony/security-core', '6.3', 'Passing users that are not instance of "%s" to "%s" is deprecated, "%s" given.', InMemoryUser::class, __METHOD__, get_debug_type($user)); + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } $userIdentifier = strtolower($user->getUserIdentifier()); @@ -93,13 +91,11 @@ public function supportsClass(string $class): bool } /** - * Returns the user by given username. - * - * @return InMemoryUser change return type on 7.0 + * Returns the user by given user. * * @throws UserNotFoundException if user whose given username does not exist */ - private function getUser(string $username): UserInterface + private function getUser(string $username): InMemoryUser { if (!isset($this->users[strtolower($username)])) { $ex = new UserNotFoundException(sprintf('Username "%s" does not exist.', $username)); From d888d4a7f9ffff7cb0b969ea4ee7abd00e4b8e4b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Jul 2023 14:50:59 +0200 Subject: [PATCH 088/138] [7.0] Remove remaining deprecated code paths --- CHANGELOG.md | 6 +++++ Security.php | 69 ---------------------------------------------------- 2 files changed, 6 insertions(+), 69 deletions(-) delete mode 100644 Security.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b489556c..663b5999 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +7.0 +--- + + * Remove the `Security` class, use `Symfony\Bundle\SecurityBundle\Security` instead + * Require explicit argument when calling `TokenStorage::setToken()` + 6.3 --- diff --git a/Security.php b/Security.php deleted file mode 100644 index bb2576a7..00000000 --- a/Security.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core; - -use Psr\Container\ContainerInterface; -use Symfony\Bundle\SecurityBundle\Security as NewSecurityHelper; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Component\Security\Core\User\UserInterface; - -/** - * Helper class for commonly-needed security tasks. - * - * @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security instead - */ -class Security implements AuthorizationCheckerInterface -{ - public const ACCESS_DENIED_ERROR = '_security.403_error'; - public const AUTHENTICATION_ERROR = '_security.last_error'; - public const LAST_USERNAME = '_security.last_username'; - - /** - * @deprecated since Symfony 6.2, use \Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge::MAX_USERNAME_LENGTH instead - */ - public const MAX_USERNAME_LENGTH = 4096; - - private ContainerInterface $container; - - public function __construct(ContainerInterface $container, bool $triggerDeprecation = true) - { - $this->container = $container; - - if ($triggerDeprecation) { - trigger_deprecation('symfony/security-core', '6.2', 'The "%s" class is deprecated, use "%s" instead.', __CLASS__, NewSecurityHelper::class); - } - } - - public function getUser(): ?UserInterface - { - if (!$token = $this->getToken()) { - return null; - } - - return $token->getUser(); - } - - /** - * Checks if the attributes are granted against the current authentication token and optionally supplied subject. - */ - public function isGranted(mixed $attributes, mixed $subject = null): bool - { - return $this->container->get('security.authorization_checker') - ->isGranted($attributes, $subject); - } - - public function getToken(): ?TokenInterface - { - return $this->container->get('security.token_storage')->getToken(); - } -} From e267f60fb5549032781a48ec6c4fb6ce6f73ebdb Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 23:52:21 +0200 Subject: [PATCH 089/138] [Components] Convert to native return types --- .../RememberMe/InMemoryTokenProvider.php | 15 +++----------- .../RememberMe/TokenProviderInterface.php | 16 ++++----------- Authentication/Token/AbstractToken.php | 20 ++++--------------- Authentication/Token/NullToken.php | 20 ++++--------------- Authentication/Token/Storage/TokenStorage.php | 10 ++-------- .../Token/Storage/TokenStorageInterface.php | 4 +--- Authentication/Token/TokenInterface.php | 17 ++++------------ Authorization/Voter/RoleHierarchyVoter.php | 5 +---- Authorization/Voter/RoleVoter.php | 5 +---- Event/AuthenticationEvent.php | 5 +---- Exception/AccessDeniedException.php | 10 ++-------- Exception/AccountStatusException.php | 5 +---- Exception/AuthenticationException.php | 9 ++------- ...ustomUserMessageAccountStatusException.php | 4 +--- ...stomUserMessageAuthenticationException.php | 4 +--- Role/RoleHierarchy.php | 5 +---- User/InMemoryUserChecker.php | 10 ++-------- User/InMemoryUserProvider.php | 4 +--- User/UserCheckerInterface.php | 8 ++------ User/UserInterface.php | 4 +--- User/UserProviderInterface.php | 8 ++------ .../Constraints/UserPasswordValidator.php | 5 +---- 22 files changed, 42 insertions(+), 151 deletions(-) diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 341883f3..1cc6ded5 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -31,10 +31,7 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface return $this->tokens[$series]; } - /** - * @return void - */ - public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed): void { if (!isset($this->tokens[$series])) { throw new TokenNotFoundException('No token found.'); @@ -50,18 +47,12 @@ public function updateToken(string $series, #[\SensitiveParameter] string $token $this->tokens[$series] = $token; } - /** - * @return void - */ - public function deleteTokenBySeries(string $series) + public function deleteTokenBySeries(string $series): void { unset($this->tokens[$series]); } - /** - * @return void - */ - public function createNewToken(PersistentTokenInterface $token) + public function createNewToken(PersistentTokenInterface $token): void { $this->tokens[$token->getSeries()] = $token; } diff --git a/Authentication/RememberMe/TokenProviderInterface.php b/Authentication/RememberMe/TokenProviderInterface.php index cf41e0c5..8be82c26 100644 --- a/Authentication/RememberMe/TokenProviderInterface.php +++ b/Authentication/RememberMe/TokenProviderInterface.php @@ -23,32 +23,24 @@ interface TokenProviderInterface /** * Loads the active token for the given series. * - * @return PersistentTokenInterface - * * @throws TokenNotFoundException if the token is not found */ - public function loadTokenBySeries(string $series); + public function loadTokenBySeries(string $series): PersistentTokenInterface; /** * Deletes all tokens belonging to series. - * - * @return void */ - public function deleteTokenBySeries(string $series); + public function deleteTokenBySeries(string $series): void; /** * Updates the token according to this data. * - * @return void - * * @throws TokenNotFoundException if the token is not found */ - public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed); + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed): void; /** * Creates a new token. - * - * @return void */ - public function createNewToken(PersistentTokenInterface $token); + public function createNewToken(PersistentTokenInterface $token): void; } diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 10cb8f77..36d64766 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -53,18 +53,12 @@ public function getUser(): ?UserInterface return $this->user; } - /** - * @return void - */ - public function setUser(UserInterface $user) + public function setUser(UserInterface $user): void { $this->user = $user; } - /** - * @return void - */ - public function eraseCredentials() + public function eraseCredentials(): void { if ($this->getUser() instanceof UserInterface) { $this->getUser()->eraseCredentials(); @@ -118,10 +112,7 @@ public function getAttributes(): array return $this->attributes; } - /** - * @return void - */ - public function setAttributes(array $attributes) + public function setAttributes(array $attributes): void { $this->attributes = $attributes; } @@ -140,10 +131,7 @@ public function getAttribute(string $name): mixed return $this->attributes[$name]; } - /** - * @return void - */ - public function setAttribute(string $name, mixed $value) + public function setAttribute(string $name, mixed $value): void { $this->attributes[$name] = $value; } diff --git a/Authentication/Token/NullToken.php b/Authentication/Token/NullToken.php index eabfe17b..9c2e4892 100644 --- a/Authentication/Token/NullToken.php +++ b/Authentication/Token/NullToken.php @@ -33,10 +33,7 @@ public function getUser(): ?UserInterface return null; } - /** - * @return never - */ - public function setUser(UserInterface $user) + public function setUser(UserInterface $user): never { throw new \BadMethodCallException('Cannot set user on a NullToken.'); } @@ -46,10 +43,7 @@ public function getUserIdentifier(): string return ''; } - /** - * @return void - */ - public function eraseCredentials() + public function eraseCredentials(): void { } @@ -58,10 +52,7 @@ public function getAttributes(): array return []; } - /** - * @return never - */ - public function setAttributes(array $attributes) + public function setAttributes(array $attributes): never { throw new \BadMethodCallException('Cannot set attributes of NullToken.'); } @@ -76,10 +67,7 @@ public function getAttribute(string $name): mixed return null; } - /** - * @return never - */ - public function setAttribute(string $name, mixed $value) + public function setAttribute(string $name, mixed $value): never { throw new \BadMethodCallException('Cannot add attribute to NullToken.'); } diff --git a/Authentication/Token/Storage/TokenStorage.php b/Authentication/Token/Storage/TokenStorage.php index 8acc31bc..42234f86 100644 --- a/Authentication/Token/Storage/TokenStorage.php +++ b/Authentication/Token/Storage/TokenStorage.php @@ -37,10 +37,7 @@ public function getToken(): ?TokenInterface return $this->token; } - /** - * @return void - */ - public function setToken(?TokenInterface $token) + public function setToken(?TokenInterface $token): void { if ($token) { // ensure any initializer is called @@ -56,10 +53,7 @@ public function setInitializer(?callable $initializer): void $this->initializer = null === $initializer ? null : $initializer(...); } - /** - * @return void - */ - public function reset() + public function reset(): void { $this->setToken(null); } diff --git a/Authentication/Token/Storage/TokenStorageInterface.php b/Authentication/Token/Storage/TokenStorageInterface.php index 5fdfa4e9..0f611ca4 100644 --- a/Authentication/Token/Storage/TokenStorageInterface.php +++ b/Authentication/Token/Storage/TokenStorageInterface.php @@ -29,8 +29,6 @@ public function getToken(): ?TokenInterface; * Sets the authentication token. * * @param TokenInterface|null $token A TokenInterface token, or null if no further authentication information should be stored - * - * @return void */ - public function setToken(?TokenInterface $token); + public function setToken(?TokenInterface $token): void; } diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index d9b80ae1..6d74add1 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -50,27 +50,21 @@ public function getUser(): ?UserInterface; /** * Sets the authenticated user in the token. * - * @return void - * * @throws \InvalidArgumentException */ - public function setUser(UserInterface $user); + public function setUser(UserInterface $user): void; /** * Removes sensitive information from the token. - * - * @return void */ - public function eraseCredentials(); + public function eraseCredentials(): void; public function getAttributes(): array; /** * @param array $attributes The token attributes - * - * @return void */ - public function setAttributes(array $attributes); + public function setAttributes(array $attributes): void; public function hasAttribute(string $name): bool; @@ -79,10 +73,7 @@ public function hasAttribute(string $name): bool; */ public function getAttribute(string $name): mixed; - /** - * @return void - */ - public function setAttribute(string $name, mixed $value); + public function setAttribute(string $name, mixed $value): void; /** * Returns all the necessary state of the object for serialization purposes. diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php index c8db1485..3535ca11 100644 --- a/Authorization/Voter/RoleHierarchyVoter.php +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -31,10 +31,7 @@ public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefi parent::__construct($prefix); } - /** - * @return array - */ - protected function extractRoles(TokenInterface $token) + protected function extractRoles(TokenInterface $token): array { return $this->roleHierarchy->getReachableRoleNames($token->getRoleNames()); } diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index 70dddcff..dbf50478 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -58,10 +58,7 @@ public function supportsType(string $subjectType): bool return true; } - /** - * @return array - */ - protected function extractRoles(TokenInterface $token) + protected function extractRoles(TokenInterface $token): array { return $token->getRoleNames(); } diff --git a/Event/AuthenticationEvent.php b/Event/AuthenticationEvent.php index 054dd957..6fca50d4 100644 --- a/Event/AuthenticationEvent.php +++ b/Event/AuthenticationEvent.php @@ -28,10 +28,7 @@ public function __construct(TokenInterface $token) $this->authenticationToken = $token; } - /** - * @return TokenInterface - */ - public function getAuthenticationToken() + public function getAuthenticationToken(): TokenInterface { return $this->authenticationToken; } diff --git a/Exception/AccessDeniedException.php b/Exception/AccessDeniedException.php index c95bae03..43df719e 100644 --- a/Exception/AccessDeniedException.php +++ b/Exception/AccessDeniedException.php @@ -34,10 +34,7 @@ public function getAttributes(): array return $this->attributes; } - /** - * @return void - */ - public function setAttributes(array|string $attributes) + public function setAttributes(array|string $attributes): void { $this->attributes = (array) $attributes; } @@ -47,10 +44,7 @@ public function getSubject(): mixed return $this->subject; } - /** - * @return void - */ - public function setSubject(mixed $subject) + public function setSubject(mixed $subject): void { $this->subject = $subject; } diff --git a/Exception/AccountStatusException.php b/Exception/AccountStatusException.php index 5b064929..c0176e08 100644 --- a/Exception/AccountStatusException.php +++ b/Exception/AccountStatusException.php @@ -32,10 +32,7 @@ public function getUser(): ?UserInterface return $this->user; } - /** - * @return void - */ - public function setUser(UserInterface $user) + public function setUser(UserInterface $user): void { $this->user = $user; } diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index adec6f87..548d4853 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -39,10 +39,7 @@ public function getToken(): ?TokenInterface return $this->token; } - /** - * @return void - */ - public function setToken(TokenInterface $token) + public function setToken(TokenInterface $token): void { $this->token = $token; } @@ -90,10 +87,8 @@ public function __unserialize(array $data): void /** * Message key to be used by the translation component. - * - * @return string */ - public function getMessageKey() + public function getMessageKey(): string { return 'An authentication exception occurred.'; } diff --git a/Exception/CustomUserMessageAccountStatusException.php b/Exception/CustomUserMessageAccountStatusException.php index 829a22cd..84b1c00a 100644 --- a/Exception/CustomUserMessageAccountStatusException.php +++ b/Exception/CustomUserMessageAccountStatusException.php @@ -38,10 +38,8 @@ public function __construct(string $message = '', array $messageData = [], int $ * * @param string $messageKey The message or message key * @param array $messageData Data to be passed into the translator - * - * @return void */ - public function setSafeMessage(string $messageKey, array $messageData = []) + public function setSafeMessage(string $messageKey, array $messageData = []): void { $this->messageKey = $messageKey; $this->messageData = $messageData; diff --git a/Exception/CustomUserMessageAuthenticationException.php b/Exception/CustomUserMessageAuthenticationException.php index 40de92d2..88c5338f 100644 --- a/Exception/CustomUserMessageAuthenticationException.php +++ b/Exception/CustomUserMessageAuthenticationException.php @@ -37,10 +37,8 @@ public function __construct(string $message = '', array $messageData = [], int $ * * @param string $messageKey The message or message key * @param array $messageData Data to be passed into the translator - * - * @return void */ - public function setSafeMessage(string $messageKey, array $messageData = []) + public function setSafeMessage(string $messageKey, array $messageData = []): void { $this->messageKey = $messageKey; $this->messageData = $messageData; diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index da094d2b..acc1c251 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -49,10 +49,7 @@ public function getReachableRoleNames(array $roles): array return array_values(array_unique($reachableRoles)); } - /** - * @return void - */ - protected function buildRoleMap() + protected function buildRoleMap(): void { $this->map = []; foreach ($this->hierarchy as $main => $roles) { diff --git a/User/InMemoryUserChecker.php b/User/InMemoryUserChecker.php index a493b00e..61367c2c 100644 --- a/User/InMemoryUserChecker.php +++ b/User/InMemoryUserChecker.php @@ -20,10 +20,7 @@ */ class InMemoryUserChecker implements UserCheckerInterface { - /** - * @return void - */ - public function checkPreAuth(UserInterface $user) + public function checkPreAuth(UserInterface $user): void { if (!$user instanceof InMemoryUser) { return; @@ -36,10 +33,7 @@ public function checkPreAuth(UserInterface $user) } } - /** - * @return void - */ - public function checkPostAuth(UserInterface $user) + public function checkPostAuth(UserInterface $user): void { } } diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index 13441bc7..ff5f3595 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -49,10 +49,8 @@ public function __construct(array $users = []) /** * Adds a new User to the provider. - * - * @return void */ - public function createUser(UserInterface $user) + public function createUser(UserInterface $user): void { if (!$user instanceof InMemoryUser) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); diff --git a/User/UserCheckerInterface.php b/User/UserCheckerInterface.php index 91f21c71..480ba7b5 100644 --- a/User/UserCheckerInterface.php +++ b/User/UserCheckerInterface.php @@ -26,18 +26,14 @@ interface UserCheckerInterface /** * Checks the user account before authentication. * - * @return void - * * @throws AccountStatusException */ - public function checkPreAuth(UserInterface $user); + public function checkPreAuth(UserInterface $user): void; /** * Checks the user account after authentication. * - * @return void - * * @throws AccountStatusException */ - public function checkPostAuth(UserInterface $user); + public function checkPostAuth(UserInterface $user): void; } diff --git a/User/UserInterface.php b/User/UserInterface.php index ef22340a..50f8fb0f 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -51,10 +51,8 @@ public function getRoles(): array; * * This is important if, at any given point, sensitive information like * the plain-text password is stored on this object. - * - * @return void */ - public function eraseCredentials(); + public function eraseCredentials(): void; /** * Returns the identifier for this user (e.g. username or email address). diff --git a/User/UserProviderInterface.php b/User/UserProviderInterface.php index ec90d413..0502bfd2 100644 --- a/User/UserProviderInterface.php +++ b/User/UserProviderInterface.php @@ -39,19 +39,15 @@ interface UserProviderInterface * object can just be merged into some internal array of users / identity * map. * - * @return UserInterface - * * @throws UnsupportedUserException if the user is not supported * @throws UserNotFoundException if the user is not found */ - public function refreshUser(UserInterface $user); + public function refreshUser(UserInterface $user): UserInterface; /** * Whether this provider supports the given user class. - * - * @return bool */ - public function supportsClass(string $class); + public function supportsClass(string $class): bool; /** * Loads the user for the given user identifier (e.g. username or email). diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 41670b27..3d6c7637 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -31,10 +31,7 @@ public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherF $this->hasherFactory = $hasherFactory; } - /** - * @return void - */ - public function validate(mixed $password, Constraint $constraint) + public function validate(mixed $password, Constraint $constraint): void { if (!$constraint instanceof UserPassword) { throw new UnexpectedTypeException($constraint, UserPassword::class); From ade1448d6adf5b3987614e797685ec43e0f113bd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Jul 2023 17:25:57 +0200 Subject: [PATCH 090/138] [Security] Revert native return types on TokenProviderInterface --- .../RememberMe/TokenProviderInterface.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Authentication/RememberMe/TokenProviderInterface.php b/Authentication/RememberMe/TokenProviderInterface.php index 8be82c26..cf41e0c5 100644 --- a/Authentication/RememberMe/TokenProviderInterface.php +++ b/Authentication/RememberMe/TokenProviderInterface.php @@ -23,24 +23,32 @@ interface TokenProviderInterface /** * Loads the active token for the given series. * + * @return PersistentTokenInterface + * * @throws TokenNotFoundException if the token is not found */ - public function loadTokenBySeries(string $series): PersistentTokenInterface; + public function loadTokenBySeries(string $series); /** * Deletes all tokens belonging to series. + * + * @return void */ - public function deleteTokenBySeries(string $series): void; + public function deleteTokenBySeries(string $series); /** * Updates the token according to this data. * + * @return void + * * @throws TokenNotFoundException if the token is not found */ - public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed): void; + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed); /** * Creates a new token. + * + * @return void */ - public function createNewToken(PersistentTokenInterface $token): void; + public function createNewToken(PersistentTokenInterface $token); } From 9eb649d8f177f40c5ae54f06136251775da80da3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 10 May 2023 14:20:38 +0200 Subject: [PATCH 091/138] [Security] Make `PersistentToken` immutable and tell `TokenProviderInterface::updateToken()` implementations should accept `DateTimeInterface` --- Authentication/RememberMe/InMemoryTokenProvider.php | 4 ++++ Authentication/RememberMe/PersistentToken.php | 8 ++++---- .../RememberMe/PersistentTokenInterface.php | 2 ++ Authentication/RememberMe/TokenProviderInterface.php | 2 ++ CHANGELOG.md | 6 ++++++ .../RememberMe/CacheTokenVerifierTest.php | 10 +++++----- .../RememberMe/InMemoryTokenProviderTest.php | 8 ++++---- .../RememberMe/PersistentTokenTest.php | 12 ++++++++++-- 8 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 341883f3..1bd5234f 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -17,6 +17,8 @@ * This class is used for testing purposes, and is not really suited for production. * * @author Johannes M. Schmitt + * + * @final since Symfony 6.4 */ class InMemoryTokenProvider implements TokenProviderInterface { @@ -32,6 +34,8 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface } /** + * @param \DateTimeInterface $lastUsed Accepting only DateTime is deprecated since Symfony 6.4 + * * @return void */ public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index 79e5fc23..f473ccb7 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -22,9 +22,9 @@ final class PersistentToken implements PersistentTokenInterface private string $userIdentifier; private string $series; private string $tokenValue; - private \DateTime $lastUsed; + private \DateTimeImmutable $lastUsed; - public function __construct(string $class, string $userIdentifier, string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) + public function __construct(string $class, string $userIdentifier, string $series, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed) { if (empty($class)) { throw new \InvalidArgumentException('$class must not be empty.'); @@ -43,7 +43,7 @@ public function __construct(string $class, string $userIdentifier, string $serie $this->userIdentifier = $userIdentifier; $this->series = $series; $this->tokenValue = $tokenValue; - $this->lastUsed = $lastUsed; + $this->lastUsed = \DateTimeImmutable::createFromInterface($lastUsed); } public function getClass(): string @@ -68,6 +68,6 @@ public function getTokenValue(): string public function getLastUsed(): \DateTime { - return $this->lastUsed; + return \DateTime::createFromImmutable($this->lastUsed); } } diff --git a/Authentication/RememberMe/PersistentTokenInterface.php b/Authentication/RememberMe/PersistentTokenInterface.php index 966e689e..f5c06175 100644 --- a/Authentication/RememberMe/PersistentTokenInterface.php +++ b/Authentication/RememberMe/PersistentTokenInterface.php @@ -36,6 +36,8 @@ public function getTokenValue(): string; /** * Returns the time the token was last used. + * + * Each call SHOULD return a new distinct DateTime instance. */ public function getLastUsed(): \DateTime; diff --git a/Authentication/RememberMe/TokenProviderInterface.php b/Authentication/RememberMe/TokenProviderInterface.php index cf41e0c5..d2f0c8cb 100644 --- a/Authentication/RememberMe/TokenProviderInterface.php +++ b/Authentication/RememberMe/TokenProviderInterface.php @@ -39,6 +39,8 @@ public function deleteTokenBySeries(string $series); /** * Updates the token according to this data. * + * @param \DateTimeInterface $lastUsed Accepting only DateTime is deprecated since Symfony 6.4 + * * @return void * * @throws TokenNotFoundException if the token is not found diff --git a/CHANGELOG.md b/CHANGELOG.md index b489556c..5c56c2f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +6.4 +--- + + * Make `PersistentToken` immutable + * Deprecate accepting only `DateTime` for `TokenProviderInterface::updateToken()`, use `DateTimeInterface` instead + 6.3 --- diff --git a/Tests/Authentication/RememberMe/CacheTokenVerifierTest.php b/Tests/Authentication/RememberMe/CacheTokenVerifierTest.php index 996a42e4..4ac0588b 100644 --- a/Tests/Authentication/RememberMe/CacheTokenVerifierTest.php +++ b/Tests/Authentication/RememberMe/CacheTokenVerifierTest.php @@ -21,23 +21,23 @@ class CacheTokenVerifierTest extends TestCase public function testVerifyCurrentToken() { $verifier = new CacheTokenVerifier(new ArrayAdapter()); - $token = new PersistentToken('class', 'user', 'series1@special:chars=/', 'value', new \DateTime()); + $token = new PersistentToken('class', 'user', 'series1@special:chars=/', 'value', new \DateTimeImmutable()); $this->assertTrue($verifier->verifyToken($token, 'value')); } public function testVerifyFailsInvalidToken() { $verifier = new CacheTokenVerifier(new ArrayAdapter()); - $token = new PersistentToken('class', 'user', 'series1@special:chars=/', 'value', new \DateTime()); + $token = new PersistentToken('class', 'user', 'series1@special:chars=/', 'value', new \DateTimeImmutable()); $this->assertFalse($verifier->verifyToken($token, 'wrong-value')); } public function testVerifyOutdatedToken() { $verifier = new CacheTokenVerifier(new ArrayAdapter()); - $outdatedToken = new PersistentToken('class', 'user', 'series1@special:chars=/', 'value', new \DateTime()); - $newToken = new PersistentToken('class', 'user', 'series1@special:chars=/', 'newvalue', new \DateTime()); - $verifier->updateExistingToken($outdatedToken, 'newvalue', new \DateTime()); + $outdatedToken = new PersistentToken('class', 'user', 'series1@special:chars=/', 'value', new \DateTimeImmutable()); + $newToken = new PersistentToken('class', 'user', 'series1@special:chars=/', 'newvalue', new \DateTimeImmutable()); + $verifier->updateExistingToken($outdatedToken, 'newvalue', new \DateTimeImmutable()); $this->assertTrue($verifier->verifyToken($newToken, 'value')); } } diff --git a/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php b/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php index bbcbeb41..45fd046a 100644 --- a/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php +++ b/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php @@ -22,7 +22,7 @@ public function testCreateNewToken() { $provider = new InMemoryTokenProvider(); - $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime()); + $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTimeImmutable()); $provider->createNewToken($token); $this->assertSame($provider->loadTokenBySeries('foo'), $token); @@ -39,13 +39,13 @@ public function testUpdateToken() { $provider = new InMemoryTokenProvider(); - $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime()); + $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTimeImmutable()); $provider->createNewToken($token); $provider->updateToken('foo', 'newFoo', $lastUsed = new \DateTime()); $token = $provider->loadTokenBySeries('foo'); $this->assertEquals('newFoo', $token->getTokenValue()); - $this->assertSame($token->getLastUsed(), $lastUsed); + $this->assertEquals($token->getLastUsed(), $lastUsed); } public function testDeleteToken() @@ -53,7 +53,7 @@ public function testDeleteToken() $this->expectException(TokenNotFoundException::class); $provider = new InMemoryTokenProvider(); - $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTime()); + $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTimeImmutable()); $provider->createNewToken($token); $provider->deleteTokenBySeries('foo'); $provider->loadTokenBySeries('foo'); diff --git a/Tests/Authentication/RememberMe/PersistentTokenTest.php b/Tests/Authentication/RememberMe/PersistentTokenTest.php index 1b05dc41..b9701879 100644 --- a/Tests/Authentication/RememberMe/PersistentTokenTest.php +++ b/Tests/Authentication/RememberMe/PersistentTokenTest.php @@ -18,13 +18,21 @@ class PersistentTokenTest extends TestCase { public function testConstructor() { - $lastUsed = new \DateTime(); + $lastUsed = new \DateTimeImmutable(); $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed); $this->assertEquals('fooclass', $token->getClass()); $this->assertEquals('fooname', $token->getUserIdentifier()); $this->assertEquals('fooseries', $token->getSeries()); $this->assertEquals('footokenvalue', $token->getTokenValue()); - $this->assertSame($lastUsed, $token->getLastUsed()); + $this->assertEquals($lastUsed, $token->getLastUsed()); + } + + public function testDateTime() + { + $lastUsed = new \DateTime(); + $token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed); + + $this->assertEquals($lastUsed, $token->getLastUsed()); } } From 9e130f3a4eddc81e81b860c6f9601feff211553b Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 24 Jul 2023 16:03:38 +0200 Subject: [PATCH 092/138] [Validator] Remove Doctrine annotations support --- Validator/Constraints/UserPassword.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Validator/Constraints/UserPassword.php b/Validator/Constraints/UserPassword.php index b1a340c8..e7b7cd26 100644 --- a/Validator/Constraints/UserPassword.php +++ b/Validator/Constraints/UserPassword.php @@ -13,10 +13,6 @@ use Symfony\Component\Validator\Constraint; -/** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - */ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class UserPassword extends Constraint { From 7c0d7caac8406163eea0ed349f110ed262ffd662 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 15:28:24 +0200 Subject: [PATCH 093/138] Use typed properties in tests as much as possible --- .../Authentication/Token/AbstractTokenTest.php | 2 +- .../Authorization/AccessDecisionManagerTest.php | 2 +- .../Authorization/AuthorizationCheckerTest.php | 7 ++++--- Tests/Authorization/Voter/VoterTest.php | 2 +- .../UserPasswordValidatorTestCase.php | 17 +++-------------- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Tests/Authentication/Token/AbstractTokenTest.php b/Tests/Authentication/Token/AbstractTokenTest.php index dc67f22f..221df8f9 100644 --- a/Tests/Authentication/Token/AbstractTokenTest.php +++ b/Tests/Authentication/Token/AbstractTokenTest.php @@ -96,7 +96,7 @@ public function testSetUser($user) class ConcreteToken extends AbstractToken { - private $credentials = 'credentials_value'; + private string $credentials = 'credentials_value'; public function __construct(array $roles = [], UserInterface $user = null) { diff --git a/Tests/Authorization/AccessDecisionManagerTest.php b/Tests/Authorization/AccessDecisionManagerTest.php index 41b772d5..37848ab7 100644 --- a/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Tests/Authorization/AccessDecisionManagerTest.php @@ -255,7 +255,7 @@ protected static function getVoters($grants, $denies, $abstains): array protected static function getVoter($vote) { return new class($vote) implements VoterInterface { - private $vote; + private int $vote; public function __construct(int $vote) { diff --git a/Tests/Authorization/AuthorizationCheckerTest.php b/Tests/Authorization/AuthorizationCheckerTest.php index 254bd978..36b048c8 100644 --- a/Tests/Authorization/AuthorizationCheckerTest.php +++ b/Tests/Authorization/AuthorizationCheckerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Tests\Authorization; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; @@ -21,9 +22,9 @@ class AuthorizationCheckerTest extends TestCase { - private $accessDecisionManager; - private $authorizationChecker; - private $tokenStorage; + private MockObject&AccessDecisionManagerInterface $accessDecisionManager; + private AuthorizationChecker $authorizationChecker; + private TokenStorage $tokenStorage; protected function setUp(): void { diff --git a/Tests/Authorization/Voter/VoterTest.php b/Tests/Authorization/Voter/VoterTest.php index 25aff8e1..80c3f4a0 100644 --- a/Tests/Authorization/Voter/VoterTest.php +++ b/Tests/Authorization/Voter/VoterTest.php @@ -18,7 +18,7 @@ class VoterTest extends TestCase { - protected $token; + protected TokenInterface $token; protected function setUp(): void { diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php index e3f71b70..ccf556a0 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php @@ -29,20 +29,9 @@ abstract class UserPasswordValidatorTestCase extends ConstraintValidatorTestCase private const PASSWORD = 's3Cr3t'; private const SALT = '^S4lt$'; - /** - * @var TokenStorageInterface - */ - protected $tokenStorage; - - /** - * @var PasswordHasherInterface - */ - protected $hasher; - - /** - * @var PasswordHasherFactoryInterface - */ - protected $hasherFactory; + protected TokenStorageInterface $tokenStorage; + protected PasswordHasherInterface $hasher; + protected PasswordHasherFactoryInterface $hasherFactory; protected function createValidator(): UserPasswordValidator { From 43f24cf63b1a21d302423a127f601e9fd0fd7535 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 18:41:43 +0200 Subject: [PATCH 094/138] Add types to private and internal properties --- Test/AccessDecisionStrategyTestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/AccessDecisionStrategyTestCase.php b/Test/AccessDecisionStrategyTestCase.php index d542588f..bf2a2b9a 100644 --- a/Test/AccessDecisionStrategyTestCase.php +++ b/Test/AccessDecisionStrategyTestCase.php @@ -64,7 +64,7 @@ final protected static function getVoters(int $grants, int $denies, int $abstain final protected static function getVoter(int $vote): VoterInterface { return new class($vote) implements VoterInterface { - private $vote; + private int $vote; public function __construct(int $vote) { From 86dc400fa69243439149895ad653c74211ac17c8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 26 Jul 2023 17:12:55 +0200 Subject: [PATCH 095/138] More short closures + isset instead of null checks + etc. --- Exception/AuthenticationException.php | 28 --------------------------- 1 file changed, 28 deletions(-) diff --git a/Exception/AuthenticationException.php b/Exception/AuthenticationException.php index adec6f87..a69155e1 100644 --- a/Exception/AuthenticationException.php +++ b/Exception/AuthenticationException.php @@ -23,17 +23,8 @@ #[WithHttpStatus(401)] class AuthenticationException extends RuntimeException { - /** @internal */ - protected $serialized; - private ?TokenInterface $token = null; - public function __construct(string $message = '', int $code = 0, \Throwable $previous = null) - { - unset($this->serialized); - parent::__construct($message, $code, $previous); - } - public function getToken(): ?TokenInterface { return $this->token; @@ -105,23 +96,4 @@ public function getMessageData(): array { return []; } - - /** - * @internal - */ - public function __sleep(): array - { - $this->serialized = $this->__serialize(); - - return ['serialized']; - } - - /** - * @internal - */ - public function __wakeup(): void - { - $this->__unserialize($this->serialized); - unset($this->serialized); - } } From ee0949879d3b05b61c30e710e03f2354ccdd8a91 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 15:36:26 +0200 Subject: [PATCH 096/138] Add types to public and protected properties --- Role/RoleHierarchy.php | 5 +++-- Validator/Constraints/UserPassword.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index acc1c251..15c5750d 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -18,9 +18,10 @@ */ class RoleHierarchy implements RoleHierarchyInterface { - private array $hierarchy; /** @var array> */ - protected $map; + protected array $map; + + private array $hierarchy; /** * @param array> $hierarchy diff --git a/Validator/Constraints/UserPassword.php b/Validator/Constraints/UserPassword.php index e7b7cd26..6f4024c0 100644 --- a/Validator/Constraints/UserPassword.php +++ b/Validator/Constraints/UserPassword.php @@ -22,8 +22,8 @@ class UserPassword extends Constraint self::INVALID_PASSWORD_ERROR => 'INVALID_PASSWORD_ERROR', ]; - public $message = 'This value should be the user\'s current password.'; - public $service = 'security.validator.user_password'; + public string $message = 'This value should be the user\'s current password.'; + public string $service = 'security.validator.user_password'; public function __construct(array $options = null, string $message = null, string $service = null, array $groups = null, mixed $payload = null) { From b9d701ec60ef8da6c223c8fc778eeccbacb7c3f7 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 28 Jul 2023 16:03:16 +0200 Subject: [PATCH 097/138] Use Stringable interface as much as possible --- Authentication/Token/TokenInterface.php | 2 +- User/InMemoryUser.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Authentication/Token/TokenInterface.php b/Authentication/Token/TokenInterface.php index d9b80ae1..7d880056 100644 --- a/Authentication/Token/TokenInterface.php +++ b/Authentication/Token/TokenInterface.php @@ -19,7 +19,7 @@ * @author Fabien Potencier * @author Johannes M. Schmitt */ -interface TokenInterface +interface TokenInterface extends \Stringable { /** * Returns a string representation of the Token. diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index b4fc8f1d..c319e1f9 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -19,7 +19,7 @@ * @author Robin Chalas * @author Fabien Potencier */ -final class InMemoryUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface +final class InMemoryUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface, \Stringable { private string $username; private ?string $password; From b86ce012cc9a62a15ed43af5037eebc3e6de4d7f Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Fri, 28 Jul 2023 17:01:18 +0200 Subject: [PATCH 098/138] Fix symfony/deprecation-contracts require --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 48dd96f9..6117ecaa 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", "symfony/password-hasher": "^5.4|^6.0" From 4284c1ff806c4efefd5e65741d8e10e48412c3bc Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 23 May 2023 13:21:41 +0200 Subject: [PATCH 099/138] Add generic to UserProviderInterface --- User/AttributesBasedUserProviderInterface.php | 6 ++++++ User/ChainUserProvider.php | 2 ++ User/InMemoryUserProvider.php | 2 ++ User/MissingUserProvider.php | 2 ++ User/UserProviderInterface.php | 6 ++++++ 5 files changed, 18 insertions(+) diff --git a/User/AttributesBasedUserProviderInterface.php b/User/AttributesBasedUserProviderInterface.php index 10cbb434..201f6bc7 100644 --- a/User/AttributesBasedUserProviderInterface.php +++ b/User/AttributesBasedUserProviderInterface.php @@ -18,6 +18,10 @@ * This is particularly useful with self-contained access tokens. * * @experimental + * + * @template-covariant TUser of UserInterface + * + * @template-extends UserProviderInterface */ interface AttributesBasedUserProviderInterface extends UserProviderInterface { @@ -26,6 +30,8 @@ interface AttributesBasedUserProviderInterface extends UserProviderInterface * * This method must throw UserNotFoundException if the user is not found. * + * @return TUser + * * @throws UserNotFoundException */ public function loadUserByIdentifier(string $identifier, array $attributes = []): UserInterface; diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 47ebbc1a..9bac82d7 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -21,6 +21,8 @@ * handle the request. * * @author Johannes M. Schmitt + * + * @template-implements UserProviderInterface */ class ChainUserProvider implements UserProviderInterface, PasswordUpgraderInterface { diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index e0aef90a..07e6cd00 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -21,6 +21,8 @@ * (a backend with a unique admin for instance) * * @author Fabien Potencier + * + * @template-implements UserProviderInterface */ class InMemoryUserProvider implements UserProviderInterface { diff --git a/User/MissingUserProvider.php b/User/MissingUserProvider.php index 4d389ffc..cf6102aa 100644 --- a/User/MissingUserProvider.php +++ b/User/MissingUserProvider.php @@ -18,6 +18,8 @@ * when a firewall requires a user provider but none was defined. * * @internal + * + * @template-implements UserProviderInterface */ class MissingUserProvider implements UserProviderInterface { diff --git a/User/UserProviderInterface.php b/User/UserProviderInterface.php index ec90d413..0a4d562d 100644 --- a/User/UserProviderInterface.php +++ b/User/UserProviderInterface.php @@ -28,6 +28,8 @@ * information is submitted or what the UserInterface object looks like. * * @author Fabien Potencier + * + * @template-covariant TUser of UserInterface */ interface UserProviderInterface { @@ -41,6 +43,8 @@ interface UserProviderInterface * * @return UserInterface * + * @psalm-return TUser + * * @throws UnsupportedUserException if the user is not supported * @throws UserNotFoundException if the user is not found */ @@ -58,6 +62,8 @@ public function supportsClass(string $class); * * This method must throw UserNotFoundException if the user is not found. * + * @return TUser + * * @throws UserNotFoundException */ public function loadUserByIdentifier(string $identifier): UserInterface; From 2e2d79bb186332cf1b92d87f242764503213911e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 3 Aug 2023 13:01:57 +0200 Subject: [PATCH 100/138] Remove remaining experimental classes --- User/AttributesBasedUserProviderInterface.php | 2 -- User/OidcUser.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/User/AttributesBasedUserProviderInterface.php b/User/AttributesBasedUserProviderInterface.php index 201f6bc7..9d79422a 100644 --- a/User/AttributesBasedUserProviderInterface.php +++ b/User/AttributesBasedUserProviderInterface.php @@ -17,8 +17,6 @@ * Overrides UserProviderInterface to add an "attributes" argument on loadUserByIdentifier. * This is particularly useful with self-contained access tokens. * - * @experimental - * * @template-covariant TUser of UserInterface * * @template-extends UserProviderInterface diff --git a/User/OidcUser.php b/User/OidcUser.php index c88d34b7..490a3ec2 100644 --- a/User/OidcUser.php +++ b/User/OidcUser.php @@ -13,8 +13,6 @@ /** * UserInterface implementation used by the access-token security workflow with an OIDC server. - * - * @experimental */ class OidcUser implements UserInterface { From 10a13dd18d5cff64ff90e8c0afad9f9fa4895f48 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 5 Aug 2023 11:18:22 +0200 Subject: [PATCH 101/138] Add 2 recently missing return types --- Authentication/RememberMe/InMemoryTokenProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 0aef6a2c..5a1f5865 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -31,7 +31,7 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface return $this->tokens[$series]; } - public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed) + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed): void { if (!isset($this->tokens[$series])) { throw new TokenNotFoundException('No token found.'); From 439c9ea751ea4b4f575c7ffd6a46f4c543a032f7 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 5 Aug 2023 12:32:37 +0200 Subject: [PATCH 102/138] [Security] Do not make PasswordUpgraderInterface a generic --- User/PasswordUpgraderInterface.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/User/PasswordUpgraderInterface.php b/User/PasswordUpgraderInterface.php index 6bf18c7f..fd21f14a 100644 --- a/User/PasswordUpgraderInterface.php +++ b/User/PasswordUpgraderInterface.php @@ -13,8 +13,6 @@ /** * @author Nicolas Grekas - * - * @template TUser of PasswordAuthenticatedUserInterface */ interface PasswordUpgraderInterface { @@ -24,8 +22,6 @@ interface PasswordUpgraderInterface * This method should persist the new password in the user storage and update the $user object accordingly. * Because you don't want your users not being able to log in, this method should be opportunistic: * it's fine if it does nothing or if it fails without throwing any exception. - * - * @param TUser $user */ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void; } From f2386194ba9392e5f81444dcf0776d0785c8329a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 18 Aug 2023 14:20:46 +0200 Subject: [PATCH 103/138] [FrameworkBundle][Validator] Deprecate annotation occurrences --- Tests/Validator/Constraints/UserPasswordTest.php | 6 +++--- composer.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Validator/Constraints/UserPasswordTest.php b/Tests/Validator/Constraints/UserPasswordTest.php index fae450b6..ed4ca442 100644 --- a/Tests/Validator/Constraints/UserPasswordTest.php +++ b/Tests/Validator/Constraints/UserPasswordTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; use Symfony\Component\Validator\Mapping\ClassMetadata; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Validator\Mapping\Loader\AttributeLoader; class UserPasswordTest extends TestCase { @@ -40,7 +40,7 @@ public static function provideServiceValidatedConstraints(): iterable yield 'named arguments' => [new UserPassword(service: 'my_service')]; $metadata = new ClassMetadata(UserPasswordDummy::class); - self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata)); + self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); yield 'attribute' => [$metadata->properties['b']->constraints[0]]; } @@ -48,7 +48,7 @@ public static function provideServiceValidatedConstraints(): iterable public function testAttributes() { $metadata = new ClassMetadata(UserPasswordDummy::class); - self::assertTrue((new AnnotationLoader())->loadClassMetadata($metadata)); + self::assertTrue((new AttributeLoader())->loadClassMetadata($metadata)); [$bConstraint] = $metadata->properties['b']->getConstraints(); self::assertSame('myMessage', $bConstraint->message); diff --git a/composer.json b/composer.json index dc1e1951..62d386a5 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "symfony/ldap": "^5.4|^6.0|^7.0", "symfony/string": "^5.4|^6.0|^7.0", "symfony/translation": "^5.4|^6.0|^7.0", - "symfony/validator": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", "psr/log": "^1|^2|^3" }, "conflict": { From 354c668d9b15a35fedfccb5d2da5d7444b6dcaa7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Aug 2023 18:21:02 +0200 Subject: [PATCH 104/138] =?UTF-8?q?Update=20the=20list=20of=20sponsors=20f?= =?UTF-8?q?or=20Symfony=206.4=20=F0=9F=99=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7385af8..48ffb0e5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 6.3 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.4 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video From 0aa85ec9a269942b2552ca080e9728d3cc140a85 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sun, 27 Aug 2023 15:53:12 +0200 Subject: [PATCH 105/138] Remove getUsername methods in tests --- Tests/Authentication/AuthenticationTrustResolverTest.php | 4 ---- Tests/Authentication/Token/Fixtures/CustomUser.php | 5 ----- 2 files changed, 9 deletions(-) diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index 02149ce3..3e0a8d50 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -115,10 +115,6 @@ public function setUser($user): void { } - public function getUsername(): string - { - } - public function getUserIdentifier(): string { } diff --git a/Tests/Authentication/Token/Fixtures/CustomUser.php b/Tests/Authentication/Token/Fixtures/CustomUser.php index 52fea7a3..99302032 100644 --- a/Tests/Authentication/Token/Fixtures/CustomUser.php +++ b/Tests/Authentication/Token/Fixtures/CustomUser.php @@ -17,11 +17,6 @@ public function __construct(string $username, array $roles) $this->roles = $roles; } - public function getUsername(): string - { - return $this->username; - } - public function getUserIdentifier(): string { return $this->username; From 1dde6ee8433759b7e20bc6580d25b2dc39594a73 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Fri, 1 Sep 2023 22:31:43 +0200 Subject: [PATCH 106/138] [DoctrineBridge][Security] Rename loadUserByUsername tests to loadUserByIdentifier --- Tests/User/ChainUserProviderTest.php | 4 ++-- Tests/User/InMemoryUserProviderTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/User/ChainUserProviderTest.php b/Tests/User/ChainUserProviderTest.php index a5a74f0b..09227752 100644 --- a/Tests/User/ChainUserProviderTest.php +++ b/Tests/User/ChainUserProviderTest.php @@ -23,7 +23,7 @@ class ChainUserProviderTest extends TestCase { - public function testLoadUserByUsername() + public function testLoadUserByIdentifier() { $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 @@ -45,7 +45,7 @@ public function testLoadUserByUsername() $this->assertSame($account, $provider->loadUserByIdentifier('foo')); } - public function testLoadUserByUsernameThrowsUserNotFoundException() + public function testLoadUserByIdentifierThrowsUserNotFoundException() { $this->expectException(UserNotFoundException::class); $provider1 = $this->createMock(InMemoryUserProvider::class); diff --git a/Tests/User/InMemoryUserProviderTest.php b/Tests/User/InMemoryUserProviderTest.php index da0d832d..1a843e4e 100644 --- a/Tests/User/InMemoryUserProviderTest.php +++ b/Tests/User/InMemoryUserProviderTest.php @@ -68,7 +68,7 @@ public function testCreateUserAlreadyExist() $provider->createUser(new InMemoryUser('fabien', 'foo')); } - public function testLoadUserByUsernameDoesNotExist() + public function testLoadUserByIdentifierDoesNotExist() { $this->expectException(UserNotFoundException::class); $provider = new InMemoryUserProvider(); From e399e6ab641ccbb1e1d112a0a28aa7bf523b44fc Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Fri, 1 Sep 2023 22:45:38 +0200 Subject: [PATCH 107/138] [Security] alway use getUserIdentifier in InMemoryTokenProvider --- Authentication/RememberMe/InMemoryTokenProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/RememberMe/InMemoryTokenProvider.php b/Authentication/RememberMe/InMemoryTokenProvider.php index 341883f3..61287054 100644 --- a/Authentication/RememberMe/InMemoryTokenProvider.php +++ b/Authentication/RememberMe/InMemoryTokenProvider.php @@ -42,7 +42,7 @@ public function updateToken(string $series, #[\SensitiveParameter] string $token $token = new PersistentToken( $this->tokens[$series]->getClass(), - method_exists($this->tokens[$series], 'getUserIdentifier') ? $this->tokens[$series]->getUserIdentifier() : $this->tokens[$series]->getUsername(), + $this->tokens[$series]->getUserIdentifier(), $series, $tokenValue, $lastUsed From e0b8cbdcb2dea1a8a42a97414c74d759f6b7670b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 18 Oct 2023 14:57:55 +0200 Subject: [PATCH 108/138] [7.0] Cleanup legacy code paths --- Authentication/RememberMe/TokenProviderInterface.php | 4 +--- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Authentication/RememberMe/TokenProviderInterface.php b/Authentication/RememberMe/TokenProviderInterface.php index d2f0c8cb..bfe49015 100644 --- a/Authentication/RememberMe/TokenProviderInterface.php +++ b/Authentication/RememberMe/TokenProviderInterface.php @@ -39,13 +39,11 @@ public function deleteTokenBySeries(string $series); /** * Updates the token according to this data. * - * @param \DateTimeInterface $lastUsed Accepting only DateTime is deprecated since Symfony 6.4 - * * @return void * * @throws TokenNotFoundException if the token is not found */ - public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed); + public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed); /** * Creates a new token. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a451ccc..47b4a210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Remove the `Security` class, use `Symfony\Bundle\SecurityBundle\Security` instead * Require explicit argument when calling `TokenStorage::setToken()` + * Change argument `$lastUsed` of `TokenProviderInterface::updateToken()` to accept `DateTimeInterface` 6.4 --- From 9e24a7199744d944c03fc1448276dc57f6237a33 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 6 Nov 2023 16:34:32 +0100 Subject: [PATCH 109/138] Check whether secrets are empty and mark them all as sensitive --- Authentication/Token/RememberMeToken.php | 9 +++++---- Signature/SignatureHasher.php | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index cf99502e..ad218f1b 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Authentication\Token; +use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\User\UserInterface; /** @@ -32,12 +33,12 @@ public function __construct(UserInterface $user, string $firewallName, #[\Sensit { parent::__construct($user->getRoles()); - if (empty($secret)) { - throw new \InvalidArgumentException('$secret must not be empty.'); + if (!$secret) { + throw new InvalidArgumentException('A non-empty secret is required.'); } - if ('' === $firewallName) { - throw new \InvalidArgumentException('$firewallName must not be empty.'); + if (!$firewallName) { + throw new InvalidArgumentException('$firewallName must not be empty.'); } $this->firewallName = $firewallName; diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index aede020e..73dcbb41 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Core\Signature; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\Security\Core\Exception\InvalidArgumentException; use Symfony\Component\Security\Core\Signature\Exception\ExpiredSignatureException; use Symfony\Component\Security\Core\Signature\Exception\InvalidSignatureException; use Symfony\Component\Security\Core\User\UserInterface; @@ -37,6 +38,10 @@ class SignatureHasher */ public function __construct(PropertyAccessorInterface $propertyAccessor, array $signatureProperties, #[\SensitiveParameter] string $secret, ExpiredSignatureStorage $expiredSignaturesStorage = null, int $maxUses = null) { + if (!$secret) { + throw new InvalidArgumentException('A non-empty secret is required.'); + } + $this->propertyAccessor = $propertyAccessor; $this->signatureProperties = $signatureProperties; $this->secret = $secret; From 7363e581fad8b0037ade4b0c803af7d11fd5066f Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 20 Nov 2023 22:20:58 +0100 Subject: [PATCH 110/138] [Security] remove conflict with symfony/security-guard --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 0bc2c140..51323362 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,6 @@ "conflict": { "symfony/event-dispatcher": "<6.4", "symfony/http-foundation": "<6.4", - "symfony/security-guard": "<6.4", "symfony/ldap": "<6.4", "symfony/validator": "<6.4" }, From 2ba040de8e6d93e07edc7307dc75b42e06137405 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 30 Nov 2023 12:04:23 +0100 Subject: [PATCH 111/138] Update sponsors of Symfony 7.0: Shopware, Sulu and Les-Tilleuls + Sensiolabs for Messenger & SymfonyCasts for Security components --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48ffb0e5..5bb87c3c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 6.4 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 7.0 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video From aedbe2a4fe792f6a44b965b9f0e461e81ac51d25 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 8 Dec 2023 15:23:08 +0100 Subject: [PATCH 112/138] Fx README files --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5bb87c3c..b7068290 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ so called user providers that hold the users credentials. Getting Started --------------- -``` -$ composer require symfony/security-core +```bash +composer require symfony/security-core ``` ```php From bb53c52b1063ac9bc850ff58d6b58b543766cdc9 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 14 Dec 2023 11:03:37 +0100 Subject: [PATCH 113/138] Set `strict` parameter of `in_array` to true where possible --- Tests/Authorization/TraceableAccessDecisionManagerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index cefe8dbc..c14ee1fa 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -197,7 +197,7 @@ public function testAccessDecisionManagerCalledByVoter() ->expects($this->any()) ->method('vote') ->willReturnCallback(function (TokenInterface $token, $subject, array $attributes) use ($sut, $voter1) { - $vote = \in_array('attr1', $attributes) ? VoterInterface::ACCESS_GRANTED : VoterInterface::ACCESS_ABSTAIN; + $vote = \in_array('attr1', $attributes, true) ? VoterInterface::ACCESS_GRANTED : VoterInterface::ACCESS_ABSTAIN; $sut->addVoterVote($voter1, $attributes, $vote); return $vote; @@ -207,7 +207,7 @@ public function testAccessDecisionManagerCalledByVoter() ->expects($this->any()) ->method('vote') ->willReturnCallback(function (TokenInterface $token, $subject, array $attributes) use ($sut, $voter2) { - if (\in_array('attr2', $attributes)) { + if (\in_array('attr2', $attributes, true)) { $vote = null == $subject ? VoterInterface::ACCESS_GRANTED : VoterInterface::ACCESS_DENIED; } else { $vote = VoterInterface::ACCESS_ABSTAIN; @@ -222,7 +222,7 @@ public function testAccessDecisionManagerCalledByVoter() ->expects($this->any()) ->method('vote') ->willReturnCallback(function (TokenInterface $token, $subject, array $attributes) use ($sut, $voter3) { - if (\in_array('attr2', $attributes) && $subject) { + if (\in_array('attr2', $attributes, true) && $subject) { $vote = $sut->decide($token, $attributes) ? VoterInterface::ACCESS_GRANTED : VoterInterface::ACCESS_DENIED; } else { $vote = VoterInterface::ACCESS_ABSTAIN; From afa665fc5657918fbbec885b2ffdcc96e28c1333 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 18 Dec 2023 08:46:12 +0100 Subject: [PATCH 114/138] Code updates --- Authorization/Voter/RoleVoter.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index dbf50478..76de3a32 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -38,10 +38,8 @@ public function vote(TokenInterface $token, mixed $subject, array $attributes): } $result = VoterInterface::ACCESS_DENIED; - foreach ($roles as $role) { - if ($attribute === $role) { - return VoterInterface::ACCESS_GRANTED; - } + if (\in_array($attribute, $roles, true)) { + return VoterInterface::ACCESS_GRANTED; } } From e4f316c4abe3d05aab878086b82162e739b2935a Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 1 Nov 2023 09:14:07 +0100 Subject: [PATCH 115/138] [Tests] Streamline --- .../RememberMe/InMemoryTokenProviderTest.php | 7 ++++--- Tests/Authorization/Voter/VoterTest.php | 3 +-- Tests/User/ChainUserProviderTest.php | 8 ++++++-- Tests/User/InMemoryUserCheckerTest.php | 3 +-- Tests/User/InMemoryUserProviderTest.php | 7 ++++--- .../Constraints/UserPasswordValidatorTestCase.php | 3 ++- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php b/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php index 45fd046a..6fc2ab15 100644 --- a/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php +++ b/Tests/Authentication/RememberMe/InMemoryTokenProviderTest.php @@ -31,8 +31,7 @@ public function testCreateNewToken() public function testLoadTokenBySeriesThrowsNotFoundException() { $this->expectException(TokenNotFoundException::class); - $provider = new InMemoryTokenProvider(); - $provider->loadTokenBySeries('foo'); + (new InMemoryTokenProvider())->loadTokenBySeries('foo'); } public function testUpdateToken() @@ -50,12 +49,14 @@ public function testUpdateToken() public function testDeleteToken() { - $this->expectException(TokenNotFoundException::class); $provider = new InMemoryTokenProvider(); $token = new PersistentToken('foo', 'foo', 'foo', 'foo', new \DateTimeImmutable()); $provider->createNewToken($token); $provider->deleteTokenBySeries('foo'); + + $this->expectException(TokenNotFoundException::class); + $provider->loadTokenBySeries('foo'); } } diff --git a/Tests/Authorization/Voter/VoterTest.php b/Tests/Authorization/Voter/VoterTest.php index 80c3f4a0..5636340e 100644 --- a/Tests/Authorization/Voter/VoterTest.php +++ b/Tests/Authorization/Voter/VoterTest.php @@ -67,8 +67,7 @@ public function testVoteWithTypeError() { $this->expectException(\TypeError::class); $this->expectExceptionMessage('Should error'); - $voter = new TypeErrorVoterTest_Voter(); - $voter->vote($this->token, new \stdClass(), ['EDIT']); + (new TypeErrorVoterTest_Voter())->vote($this->token, new \stdClass(), ['EDIT']); } } diff --git a/Tests/User/ChainUserProviderTest.php b/Tests/User/ChainUserProviderTest.php index 09227752..90111561 100644 --- a/Tests/User/ChainUserProviderTest.php +++ b/Tests/User/ChainUserProviderTest.php @@ -47,7 +47,6 @@ public function testLoadUserByIdentifier() public function testLoadUserByIdentifierThrowsUserNotFoundException() { - $this->expectException(UserNotFoundException::class); $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) @@ -65,6 +64,9 @@ public function testLoadUserByIdentifierThrowsUserNotFoundException() ; $provider = new ChainUserProvider([$provider1, $provider2]); + + $this->expectException(UserNotFoundException::class); + $provider->loadUserByIdentifier('foo'); } @@ -141,7 +143,6 @@ public function testRefreshUserAgain() public function testRefreshUserThrowsUnsupportedUserException() { - $this->expectException(UnsupportedUserException::class); $provider1 = $this->createMock(InMemoryUserProvider::class); $provider1 ->expects($this->once()) @@ -169,6 +170,9 @@ public function testRefreshUserThrowsUnsupportedUserException() ; $provider = new ChainUserProvider([$provider1, $provider2]); + + $this->expectException(UnsupportedUserException::class); + $provider->refreshUser($this->createMock(UserInterface::class)); } diff --git a/Tests/User/InMemoryUserCheckerTest.php b/Tests/User/InMemoryUserCheckerTest.php index 8b01e5f0..25107723 100644 --- a/Tests/User/InMemoryUserCheckerTest.php +++ b/Tests/User/InMemoryUserCheckerTest.php @@ -35,7 +35,6 @@ public function testCheckPostAuthPass() public function testCheckPreAuthDisabled() { $this->expectException(DisabledException::class); - $checker = new InMemoryUserChecker(); - $checker->checkPreAuth(new InMemoryUser('John', 'password', [], false)); + (new InMemoryUserChecker())->checkPreAuth(new InMemoryUser('John', 'password', [], false)); } } diff --git a/Tests/User/InMemoryUserProviderTest.php b/Tests/User/InMemoryUserProviderTest.php index 1a843e4e..98afb3b4 100644 --- a/Tests/User/InMemoryUserProviderTest.php +++ b/Tests/User/InMemoryUserProviderTest.php @@ -62,16 +62,17 @@ public function testCreateUser() public function testCreateUserAlreadyExist() { - $this->expectException(\LogicException::class); $provider = new InMemoryUserProvider(); $provider->createUser(new InMemoryUser('fabien', 'foo')); + + $this->expectException(\LogicException::class); + $provider->createUser(new InMemoryUser('fabien', 'foo')); } public function testLoadUserByIdentifierDoesNotExist() { $this->expectException(UserNotFoundException::class); - $provider = new InMemoryUserProvider(); - $provider->loadUserByIdentifier('fabien'); + (new InMemoryUserProvider())->loadUserByIdentifier('fabien'); } } diff --git a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php index ccf556a0..c78f6b5f 100644 --- a/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php +++ b/Tests/Validator/Constraints/UserPasswordValidatorTestCase.php @@ -113,13 +113,14 @@ public static function emptyPasswordData() public function testUserIsNotValid() { - $this->expectException(ConstraintDefinitionException::class); $user = new \stdClass(); $this->tokenStorage = $this->createTokenStorage($user); $this->validator = $this->createValidator(); $this->validator->initialize($this->context); + $this->expectException(ConstraintDefinitionException::class); + $this->validator->validate('secret', new UserPassword()); } From 891b5bdad89d7331747e3501c0dee07000f6906b Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Tue, 2 Jan 2024 15:49:33 +0100 Subject: [PATCH 116/138] CS: trailing commas --- User/OidcUser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/User/OidcUser.php b/User/OidcUser.php index 490a3ec2..bcce363f 100644 --- a/User/OidcUser.php +++ b/User/OidcUser.php @@ -45,7 +45,7 @@ public function __construct( private ?\DateTimeInterface $updatedAt = null, // Additional Claims (https://openid.net/specs/openid-connect-core-1_0.html#AdditionalClaims) - ...$additionalClaims + ...$additionalClaims, ) { if (null === $sub || '' === $sub) { throw new \InvalidArgumentException('The "sub" claim cannot be empty.'); From a2eef1080aa3755699d552f6573a2ea1c881380d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 5 Jan 2024 10:21:56 +0100 Subject: [PATCH 117/138] update conflict rules --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7e5ad3d9..54a7e392 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "symfony/http-foundation": "<5.4", "symfony/security-guard": "<5.4", "symfony/ldap": "<5.4", - "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3", + "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", "symfony/validator": "<5.4" }, "autoload": { From 918585786b2ebff1d325a1872a12c0d1e3589f90 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 11 Jan 2024 11:01:49 +0100 Subject: [PATCH 118/138] do not mock the RequestStack class --- .../Storage/UsageTrackingTokenStorageTest.php | 37 ++++++------------- composer.json | 2 + 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php b/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php index 3d7c7904..72e1665a 100644 --- a/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php +++ b/Tests/Authentication/Token/Storage/UsageTrackingTokenStorageTest.php @@ -13,9 +13,10 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\Storage\UsageTrackingTokenStorage; @@ -25,28 +26,14 @@ class UsageTrackingTokenStorageTest extends TestCase { public function testGetSetToken() { - $sessionAccess = 0; - $sessionLocator = new class(['request_stack' => function () use (&$sessionAccess) { - $session = $this->createMock(SessionInterface::class); - - $request = new Request(); - $request->setSession($session); - $requestStack = $this->getMockBuilder(RequestStack::class)->onlyMethods(['getSession'])->getMock(); - $requestStack->push($request); - $requestStack->expects($this->any())->method('getSession')->willReturnCallback(function () use ($session, &$sessionAccess) { - ++$sessionAccess; - - $session->expects($this->once()) - ->method('getMetadataBag'); - - return $session; - }); - - return $requestStack; - }]) implements ContainerInterface { - use ServiceLocatorTrait; - }; $tokenStorage = new TokenStorage(); + $session = new Session(); + $request = new Request(); + $request->setSession($session); + $requestStack = new RequestStack(); + $requestStack->push($request); + $sessionLocator = new ContainerBuilder(); + $sessionLocator->set('request_stack', $requestStack); $trackingStorage = new UsageTrackingTokenStorage($tokenStorage, $sessionLocator); $this->assertNull($trackingStorage->getToken()); @@ -55,15 +42,15 @@ public function testGetSetToken() $trackingStorage->setToken($token); $this->assertSame($token, $trackingStorage->getToken()); $this->assertSame($token, $tokenStorage->getToken()); - $this->assertSame(0, $sessionAccess); + $this->assertSame(0, $session->getUsageIndex()); $trackingStorage->enableUsageTracking(); $this->assertSame($token, $trackingStorage->getToken()); - $this->assertSame(1, $sessionAccess); + $this->assertSame(1, $session->getUsageIndex()); $trackingStorage->disableUsageTracking(); $this->assertSame($token, $trackingStorage->getToken()); - $this->assertSame(1, $sessionAccess); + $this->assertSame(1, $session->getUsageIndex()); } public function testWithoutMainRequest() diff --git a/composer.json b/composer.json index bb6c4d50..a923768b 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "psr/container": "^1.1|^2.0", "psr/cache": "^1.0|^2.0|^3.0", "symfony/cache": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", @@ -35,6 +36,7 @@ "psr/log": "^1|^2|^3" }, "conflict": { + "symfony/dependency-injection": "<6.4", "symfony/event-dispatcher": "<6.4", "symfony/http-foundation": "<6.4", "symfony/ldap": "<6.4", From a2e6a338aaf4efec722839ec49f88eed2d752f92 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 Jan 2024 15:42:10 +0100 Subject: [PATCH 119/138] Fix bad merge --- Encoder/NativePasswordEncoder.php | 38 ------------------------- Encoder/PasswordHasherAdapter.php | 46 ------------------------------- Encoder/SodiumPasswordEncoder.php | 40 --------------------------- 3 files changed, 124 deletions(-) delete mode 100644 Encoder/NativePasswordEncoder.php delete mode 100644 Encoder/PasswordHasherAdapter.php delete mode 100644 Encoder/SodiumPasswordEncoder.php diff --git a/Encoder/NativePasswordEncoder.php b/Encoder/NativePasswordEncoder.php deleted file mode 100644 index eef5e62b..00000000 --- a/Encoder/NativePasswordEncoder.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\NativePasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', NativePasswordEncoder::class, NativePasswordHasher::class); - -/** - * Hashes passwords using password_hash(). - * - * @author Elnur Abdurrakhimov - * @author Terje Bråten - * @author Nicolas Grekas - * - * @deprecated since Symfony 5.3, use {@link NativePasswordHasher} instead - */ -final class NativePasswordEncoder implements PasswordEncoderInterface, SelfSaltingEncoderInterface -{ - use LegacyEncoderTrait; - - /** - * @param string|null $algo An algorithm supported by password_hash() or null to use the stronger available algorithm - */ - public function __construct(?int $opsLimit = null, ?int $memLimit = null, ?int $cost = null, ?string $algo = null) - { - $this->hasher = new NativePasswordHasher($opsLimit, $memLimit, $cost, $algo); - } -} diff --git a/Encoder/PasswordHasherAdapter.php b/Encoder/PasswordHasherAdapter.php deleted file mode 100644 index 4a4b9c0b..00000000 --- a/Encoder/PasswordHasherAdapter.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * 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\PasswordHasher\LegacyPasswordHasherInterface; - -/** - * Forward compatibility for new new PasswordHasher component. - * - * @author Alexander M. Turek - * - * @internal To be removed in Symfony 6 - */ -final class PasswordHasherAdapter implements LegacyPasswordHasherInterface -{ - private $passwordEncoder; - - public function __construct(PasswordEncoderInterface $passwordEncoder) - { - $this->passwordEncoder = $passwordEncoder; - } - - public function hash(string $plainPassword, ?string $salt = null): string - { - return $this->passwordEncoder->encodePassword($plainPassword, $salt); - } - - public function verify(string $hashedPassword, string $plainPassword, ?string $salt = null): bool - { - return $this->passwordEncoder->isPasswordValid($hashedPassword, $plainPassword, $salt); - } - - public function needsRehash(string $hashedPassword): bool - { - return $this->passwordEncoder->needsRehash($hashedPassword); - } -} diff --git a/Encoder/SodiumPasswordEncoder.php b/Encoder/SodiumPasswordEncoder.php deleted file mode 100644 index d63f5450..00000000 --- a/Encoder/SodiumPasswordEncoder.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * 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\PasswordHasher\Hasher\SodiumPasswordHasher; - -trigger_deprecation('symfony/security-core', '5.3', 'The "%s" class is deprecated, use "%s" instead.', SodiumPasswordEncoder::class, SodiumPasswordHasher::class); - -/** - * Hashes passwords using libsodium. - * - * @author Robin Chalas - * @author Zan Baldwin - * @author Dominik Müller - * - * @deprecated since Symfony 5.3, use {@link SodiumPasswordHasher} instead - */ -final class SodiumPasswordEncoder implements PasswordEncoderInterface, SelfSaltingEncoderInterface -{ - use LegacyEncoderTrait; - - public function __construct(?int $opsLimit = null, ?int $memLimit = null) - { - $this->hasher = new SodiumPasswordHasher($opsLimit, $memLimit); - } - - public static function isSupported(): bool - { - return SodiumPasswordHasher::isSupported(); - } -} From 220301f8a7cdb0b11873aa242644a6f98f893fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sun, 31 Mar 2024 15:15:18 +0200 Subject: [PATCH 120/138] Remove unnecessary empty usages --- Authentication/RememberMe/PersistentToken.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index f473ccb7..c1f1e1a7 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -26,16 +26,16 @@ final class PersistentToken implements PersistentTokenInterface public function __construct(string $class, string $userIdentifier, string $series, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed) { - if (empty($class)) { + if (!$class) { throw new \InvalidArgumentException('$class must not be empty.'); } if ('' === $userIdentifier) { throw new \InvalidArgumentException('$userIdentifier must not be empty.'); } - if (empty($series)) { + if (!$series) { throw new \InvalidArgumentException('$series must not be empty.'); } - if (empty($tokenValue)) { + if (!$tokenValue) { throw new \InvalidArgumentException('$tokenValue must not be empty.'); } From 74717b37d89469ce9206ea830e3effd2c1e75afe Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 2 May 2024 13:36:26 +0200 Subject: [PATCH 121/138] [Security] SymfonyCasts is backing the security components for v7.1, thanks to them! \o/ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7068290..fc50dcc6 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 7.0 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 7.1 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video From 74ebf9b36c60ec3881c7dcdd3d0ee17cfca0a84b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 21 May 2024 11:22:57 +0200 Subject: [PATCH 122/138] [Security] Deprecate argument $secret of RememberMeToken and RememberMeAuthenticator --- Authentication/Token/RememberMeToken.php | 20 +++++++++++-------- CHANGELOG.md | 4 ++++ .../AuthenticationTrustResolverTest.php | 2 +- .../Token/RememberMeTokenTest.php | 18 ++++++++--------- .../Authorization/ExpressionLanguageTest.php | 2 +- .../Voter/AuthenticatedVoterTest.php | 2 +- composer.json | 1 + 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index ad218f1b..643c40d4 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -21,20 +21,19 @@ */ class RememberMeToken extends AbstractToken { - private string $secret; + private ?string $secret = null; private string $firewallName; /** - * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client - * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, string $firewallName, #[\SensitiveParameter] string $secret) + public function __construct(UserInterface $user, string $firewallName) { parent::__construct($user->getRoles()); - if (!$secret) { - throw new InvalidArgumentException('A non-empty secret is required.'); + if (\func_num_args() > 2) { + trigger_deprecation('symfony/security-core', '7.2', 'The "$secret" argument of "%s()" is deprecated.', __METHOD__); + $this->secret = func_get_arg(2); } if (!$firewallName) { @@ -42,7 +41,6 @@ public function __construct(UserInterface $user, string $firewallName, #[\Sensit } $this->firewallName = $firewallName; - $this->secret = $secret; $this->setUser($user); } @@ -52,13 +50,19 @@ public function getFirewallName(): string return $this->firewallName; } + /** + * @deprecated since Symfony 7.2 + */ public function getSecret(): string { - return $this->secret; + trigger_deprecation('symfony/security-core', '7.2', 'The "%s()" method is deprecated.', __METHOD__); + + return $this->secret ??= base64_encode(random_bytes(8)); } public function __serialize(): array { + // $this->firewallName should be kept at index 1 for compatibility with payloads generated before Symfony 8 return [$this->secret, $this->firewallName, parent::__serialize()]; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b4a210..208f0d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ CHANGELOG ========= +7.2 +--- + + * Deprecate argument `$secret` of `RememberMeToken` 7.0 --- diff --git a/Tests/Authentication/AuthenticationTrustResolverTest.php b/Tests/Authentication/AuthenticationTrustResolverTest.php index 3e0a8d50..fc559983 100644 --- a/Tests/Authentication/AuthenticationTrustResolverTest.php +++ b/Tests/Authentication/AuthenticationTrustResolverTest.php @@ -72,7 +72,7 @@ protected function getRememberMeToken() { $user = new InMemoryUser('wouter', '', ['ROLE_USER']); - return new RememberMeToken($user, 'main', 'secret'); + return new RememberMeToken($user, 'main'); } } diff --git a/Tests/Authentication/Token/RememberMeTokenTest.php b/Tests/Authentication/Token/RememberMeTokenTest.php index a63d481b..b0cdbaf1 100644 --- a/Tests/Authentication/Token/RememberMeTokenTest.php +++ b/Tests/Authentication/Token/RememberMeTokenTest.php @@ -20,22 +20,22 @@ class RememberMeTokenTest extends TestCase public function testConstructor() { $user = $this->getUser(); - $token = new RememberMeToken($user, 'fookey', 'foo'); + $token = new RememberMeToken($user, 'fookey'); $this->assertEquals('fookey', $token->getFirewallName()); - $this->assertEquals('foo', $token->getSecret()); $this->assertEquals(['ROLE_FOO'], $token->getRoleNames()); $this->assertSame($user, $token->getUser()); } - public function testConstructorSecretCannotBeEmptyString() + /** + * @group legacy + */ + public function testSecret() { - $this->expectException(\InvalidArgumentException::class); - new RememberMeToken( - $this->getUser(), - '', - '' - ); + $user = $this->getUser(); + $token = new RememberMeToken($user, 'fookey', 'foo'); + + $this->assertEquals('foo', $token->getSecret()); } protected function getUser($roles = ['ROLE_FOO']) diff --git a/Tests/Authorization/ExpressionLanguageTest.php b/Tests/Authorization/ExpressionLanguageTest.php index 8cc4810a..1a4db41e 100644 --- a/Tests/Authorization/ExpressionLanguageTest.php +++ b/Tests/Authorization/ExpressionLanguageTest.php @@ -50,7 +50,7 @@ public static function provider() $user = new InMemoryUser('username', 'password', $roles); $noToken = null; - $rememberMeToken = new RememberMeToken($user, 'firewall-name', 'firewall'); + $rememberMeToken = new RememberMeToken($user, 'firewall-name'); $usernamePasswordToken = new UsernamePasswordToken($user, 'firewall-name', $roles); return [ diff --git a/Tests/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Authorization/Voter/AuthenticatedVoterTest.php index 88544c08..3a3b9d4e 100644 --- a/Tests/Authorization/Voter/AuthenticatedVoterTest.php +++ b/Tests/Authorization/Voter/AuthenticatedVoterTest.php @@ -101,7 +101,7 @@ public function getCredentials() } if ('remembered' === $authenticated) { - return new RememberMeToken($user, 'foo', 'bar'); + return new RememberMeToken($user, 'foo'); } if ('impersonated' === $authenticated) { diff --git a/composer.json b/composer.json index a923768b..0aaff1e3 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", "symfony/password-hasher": "^6.4|^7.0" From 8acc2fbaa06386de128a2a843a30728660c105c4 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 3 Jun 2024 09:09:05 +0200 Subject: [PATCH 123/138] use constructor property promotion --- .../RememberMe/CacheTokenVerifier.php | 14 +++++-------- Authentication/RememberMe/PersistentToken.php | 17 +++++++-------- .../Token/PreAuthenticatedToken.php | 10 ++++----- Authentication/Token/RememberMeToken.php | 9 ++++---- .../Storage/UsageTrackingTokenStorage.php | 10 ++++----- Authentication/Token/SwitchUserToken.php | 11 ++++++---- .../Token/UsernamePasswordToken.php | 10 ++++----- Authorization/AccessDecisionManager.php | 8 +++---- .../Strategy/AffirmativeStrategy.php | 8 +++---- Authorization/Strategy/ConsensusStrategy.php | 11 ++++------ Authorization/Strategy/PriorityStrategy.php | 8 +++---- Authorization/Strategy/UnanimousStrategy.php | 8 +++---- .../TraceableAccessDecisionManager.php | 8 +++---- Authorization/Voter/AuthenticatedVoter.php | 8 +++---- Authorization/Voter/ExpressionVoter.php | 17 ++++++--------- Authorization/Voter/RoleHierarchyVoter.php | 10 ++++----- Authorization/Voter/RoleVoter.php | 8 +++---- Authorization/Voter/TraceableVoter.php | 11 ++++------ Event/AuthenticationEvent.php | 10 ++++----- Event/VoteEvent.php | 17 ++++++--------- Exception/LazyResponseException.php | 8 +++---- ...nyLoginAttemptsAuthenticationException.php | 8 +++---- Role/RoleHierarchy.php | 9 +++----- Signature/ExpiredSignatureStorage.php | 11 ++++------ Signature/SignatureHasher.php | 21 +++++++------------ Test/AccessDecisionStrategyTestCase.php | 8 +++---- User/ChainUserProvider.php | 8 +++---- User/InMemoryUser.php | 14 ++++++------- .../Constraints/UserPasswordValidator.php | 11 ++++------ 29 files changed, 123 insertions(+), 188 deletions(-) diff --git a/Authentication/RememberMe/CacheTokenVerifier.php b/Authentication/RememberMe/CacheTokenVerifier.php index e4f1362a..3930ac8d 100644 --- a/Authentication/RememberMe/CacheTokenVerifier.php +++ b/Authentication/RememberMe/CacheTokenVerifier.php @@ -18,21 +18,17 @@ */ class CacheTokenVerifier implements TokenVerifierInterface { - private CacheItemPoolInterface $cache; - private int $outdatedTokenTtl; - private string $cacheKeyPrefix; - /** * @param int $outdatedTokenTtl How long the outdated token should still be considered valid. Defaults * to 60, which matches how often the PersistentRememberMeHandler will at * most refresh tokens. Increasing to more than that is not recommended, * but you may use a lower value. */ - public function __construct(CacheItemPoolInterface $cache, int $outdatedTokenTtl = 60, string $cacheKeyPrefix = 'rememberme-stale-') - { - $this->cache = $cache; - $this->outdatedTokenTtl = $outdatedTokenTtl; - $this->cacheKeyPrefix = $cacheKeyPrefix; + public function __construct( + private CacheItemPoolInterface $cache, + private int $outdatedTokenTtl = 60, + private string $cacheKeyPrefix = 'rememberme-stale-', + ) { } public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue): bool diff --git a/Authentication/RememberMe/PersistentToken.php b/Authentication/RememberMe/PersistentToken.php index c1f1e1a7..0f391c23 100644 --- a/Authentication/RememberMe/PersistentToken.php +++ b/Authentication/RememberMe/PersistentToken.php @@ -18,14 +18,15 @@ */ final class PersistentToken implements PersistentTokenInterface { - private string $class; - private string $userIdentifier; - private string $series; - private string $tokenValue; private \DateTimeImmutable $lastUsed; - public function __construct(string $class, string $userIdentifier, string $series, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed) - { + public function __construct( + private string $class, + private string $userIdentifier, + private string $series, + #[\SensitiveParameter] private string $tokenValue, + \DateTimeInterface $lastUsed, + ) { if (!$class) { throw new \InvalidArgumentException('$class must not be empty.'); } @@ -39,10 +40,6 @@ public function __construct(string $class, string $userIdentifier, string $serie throw new \InvalidArgumentException('$tokenValue must not be empty.'); } - $this->class = $class; - $this->userIdentifier = $userIdentifier; - $this->series = $series; - $this->tokenValue = $tokenValue; $this->lastUsed = \DateTimeImmutable::createFromInterface($lastUsed); } diff --git a/Authentication/Token/PreAuthenticatedToken.php b/Authentication/Token/PreAuthenticatedToken.php index a216d4c1..5c092404 100644 --- a/Authentication/Token/PreAuthenticatedToken.php +++ b/Authentication/Token/PreAuthenticatedToken.php @@ -20,13 +20,14 @@ */ class PreAuthenticatedToken extends AbstractToken { - private string $firewallName; - /** * @param string[] $roles */ - public function __construct(UserInterface $user, string $firewallName, array $roles = []) - { + public function __construct( + UserInterface $user, + private string $firewallName, + array $roles = [], + ) { parent::__construct($roles); if ('' === $firewallName) { @@ -34,7 +35,6 @@ public function __construct(UserInterface $user, string $firewallName, array $ro } $this->setUser($user); - $this->firewallName = $firewallName; } public function getFirewallName(): string diff --git a/Authentication/Token/RememberMeToken.php b/Authentication/Token/RememberMeToken.php index 643c40d4..dfbe20ec 100644 --- a/Authentication/Token/RememberMeToken.php +++ b/Authentication/Token/RememberMeToken.php @@ -22,13 +22,14 @@ class RememberMeToken extends AbstractToken { private ?string $secret = null; - private string $firewallName; /** * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, string $firewallName) - { + public function __construct( + UserInterface $user, + private string $firewallName, + ) { parent::__construct($user->getRoles()); if (\func_num_args() > 2) { @@ -40,8 +41,6 @@ public function __construct(UserInterface $user, string $firewallName) throw new InvalidArgumentException('$firewallName must not be empty.'); } - $this->firewallName = $firewallName; - $this->setUser($user); } diff --git a/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/Authentication/Token/Storage/UsageTrackingTokenStorage.php index 8a4069e7..4255491d 100644 --- a/Authentication/Token/Storage/UsageTrackingTokenStorage.php +++ b/Authentication/Token/Storage/UsageTrackingTokenStorage.php @@ -24,14 +24,12 @@ */ final class UsageTrackingTokenStorage implements TokenStorageInterface, ServiceSubscriberInterface { - private TokenStorageInterface $storage; - private ContainerInterface $container; private bool $enableUsageTracking = false; - public function __construct(TokenStorageInterface $storage, ContainerInterface $container) - { - $this->storage = $storage; - $this->container = $container; + public function __construct( + private TokenStorageInterface $storage, + private ContainerInterface $container, + ) { } public function getToken(): ?TokenInterface diff --git a/Authentication/Token/SwitchUserToken.php b/Authentication/Token/SwitchUserToken.php index fb632a61..c4e69766 100644 --- a/Authentication/Token/SwitchUserToken.php +++ b/Authentication/Token/SwitchUserToken.php @@ -20,7 +20,6 @@ */ class SwitchUserToken extends UsernamePasswordToken { - private TokenInterface $originalToken; private ?string $originatedFromUri = null; /** @@ -29,11 +28,15 @@ class SwitchUserToken extends UsernamePasswordToken * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, string $firewallName, array $roles, TokenInterface $originalToken, ?string $originatedFromUri = null) - { + public function __construct( + UserInterface $user, + string $firewallName, + array $roles, + private TokenInterface $originalToken, + ?string $originatedFromUri = null, + ) { parent::__construct($user, $firewallName, $roles); - $this->originalToken = $originalToken; $this->originatedFromUri = $originatedFromUri; } diff --git a/Authentication/Token/UsernamePasswordToken.php b/Authentication/Token/UsernamePasswordToken.php index 74e24a21..40beb003 100644 --- a/Authentication/Token/UsernamePasswordToken.php +++ b/Authentication/Token/UsernamePasswordToken.php @@ -20,10 +20,11 @@ */ class UsernamePasswordToken extends AbstractToken { - private string $firewallName; - - public function __construct(UserInterface $user, string $firewallName, array $roles = []) - { + public function __construct( + UserInterface $user, + private string $firewallName, + array $roles = [], + ) { parent::__construct($roles); if ('' === $firewallName) { @@ -31,7 +32,6 @@ public function __construct(UserInterface $user, string $firewallName, array $ro } $this->setUser($user); - $this->firewallName = $firewallName; } public function getFirewallName(): string diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index 4a56f943..10969acb 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -32,7 +32,6 @@ final class AccessDecisionManager implements AccessDecisionManagerInterface VoterInterface::ACCESS_ABSTAIN => true, ]; - private iterable $voters; private array $votersCacheAttributes = []; private array $votersCacheObject = []; private AccessDecisionStrategyInterface $strategy; @@ -40,9 +39,10 @@ final class AccessDecisionManager implements AccessDecisionManagerInterface /** * @param iterable $voters An array or an iterator of VoterInterface instances */ - public function __construct(iterable $voters = [], ?AccessDecisionStrategyInterface $strategy = null) - { - $this->voters = $voters; + public function __construct( + private iterable $voters = [], + ?AccessDecisionStrategyInterface $strategy = null, + ) { $this->strategy = $strategy ?? new AffirmativeStrategy(); } diff --git a/Authorization/Strategy/AffirmativeStrategy.php b/Authorization/Strategy/AffirmativeStrategy.php index ecd74b20..fb316fd3 100644 --- a/Authorization/Strategy/AffirmativeStrategy.php +++ b/Authorization/Strategy/AffirmativeStrategy.php @@ -24,11 +24,9 @@ */ final class AffirmativeStrategy implements AccessDecisionStrategyInterface, \Stringable { - private bool $allowIfAllAbstainDecisions; - - public function __construct(bool $allowIfAllAbstainDecisions = false) - { - $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; + public function __construct( + private bool $allowIfAllAbstainDecisions = false, + ) { } public function decide(\Traversable $results): bool diff --git a/Authorization/Strategy/ConsensusStrategy.php b/Authorization/Strategy/ConsensusStrategy.php index 489b3428..bff56513 100644 --- a/Authorization/Strategy/ConsensusStrategy.php +++ b/Authorization/Strategy/ConsensusStrategy.php @@ -32,13 +32,10 @@ */ final class ConsensusStrategy implements AccessDecisionStrategyInterface, \Stringable { - private bool $allowIfAllAbstainDecisions; - private bool $allowIfEqualGrantedDeniedDecisions; - - public function __construct(bool $allowIfAllAbstainDecisions = false, bool $allowIfEqualGrantedDeniedDecisions = true) - { - $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; - $this->allowIfEqualGrantedDeniedDecisions = $allowIfEqualGrantedDeniedDecisions; + public function __construct( + private bool $allowIfAllAbstainDecisions = false, + private bool $allowIfEqualGrantedDeniedDecisions = true, + ) { } public function decide(\Traversable $results): bool diff --git a/Authorization/Strategy/PriorityStrategy.php b/Authorization/Strategy/PriorityStrategy.php index 9599950c..d7f566ad 100644 --- a/Authorization/Strategy/PriorityStrategy.php +++ b/Authorization/Strategy/PriorityStrategy.php @@ -25,11 +25,9 @@ */ final class PriorityStrategy implements AccessDecisionStrategyInterface, \Stringable { - private bool $allowIfAllAbstainDecisions; - - public function __construct(bool $allowIfAllAbstainDecisions = false) - { - $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; + public function __construct( + private bool $allowIfAllAbstainDecisions = false, + ) { } public function decide(\Traversable $results): bool diff --git a/Authorization/Strategy/UnanimousStrategy.php b/Authorization/Strategy/UnanimousStrategy.php index 1f3b85c5..d47d8994 100644 --- a/Authorization/Strategy/UnanimousStrategy.php +++ b/Authorization/Strategy/UnanimousStrategy.php @@ -24,11 +24,9 @@ */ final class UnanimousStrategy implements AccessDecisionStrategyInterface, \Stringable { - private bool $allowIfAllAbstainDecisions; - - public function __construct(bool $allowIfAllAbstainDecisions = false) - { - $this->allowIfAllAbstainDecisions = $allowIfAllAbstainDecisions; + public function __construct( + private bool $allowIfAllAbstainDecisions = false, + ) { } public function decide(\Traversable $results): bool diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index cb44dce4..17db5461 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -25,17 +25,15 @@ */ class TraceableAccessDecisionManager implements AccessDecisionManagerInterface { - private AccessDecisionManagerInterface $manager; private ?AccessDecisionStrategyInterface $strategy = null; /** @var iterable */ private iterable $voters = []; private array $decisionLog = []; // All decision logs private array $currentLog = []; // Logs being filled in - public function __construct(AccessDecisionManagerInterface $manager) - { - $this->manager = $manager; - + public function __construct( + private AccessDecisionManagerInterface $manager, + ) { // The strategy and voters are stored in a private properties of the decorated service if (property_exists($manager, 'strategy')) { $reflection = new \ReflectionProperty($manager::class, 'strategy'); diff --git a/Authorization/Voter/AuthenticatedVoter.php b/Authorization/Voter/AuthenticatedVoter.php index d7b2b224..a0011868 100644 --- a/Authorization/Voter/AuthenticatedVoter.php +++ b/Authorization/Voter/AuthenticatedVoter.php @@ -33,11 +33,9 @@ class AuthenticatedVoter implements CacheableVoterInterface public const IS_REMEMBERED = 'IS_REMEMBERED'; public const PUBLIC_ACCESS = 'PUBLIC_ACCESS'; - private AuthenticationTrustResolverInterface $authenticationTrustResolver; - - public function __construct(AuthenticationTrustResolverInterface $authenticationTrustResolver) - { - $this->authenticationTrustResolver = $authenticationTrustResolver; + public function __construct( + private AuthenticationTrustResolverInterface $authenticationTrustResolver, + ) { } public function vote(TokenInterface $token, mixed $subject, array $attributes): int diff --git a/Authorization/Voter/ExpressionVoter.php b/Authorization/Voter/ExpressionVoter.php index 6de9c954..bab32830 100644 --- a/Authorization/Voter/ExpressionVoter.php +++ b/Authorization/Voter/ExpressionVoter.php @@ -26,17 +26,12 @@ */ class ExpressionVoter implements CacheableVoterInterface { - private ExpressionLanguage $expressionLanguage; - private AuthenticationTrustResolverInterface $trustResolver; - private AuthorizationCheckerInterface $authChecker; - private ?RoleHierarchyInterface $roleHierarchy; - - public function __construct(ExpressionLanguage $expressionLanguage, AuthenticationTrustResolverInterface $trustResolver, AuthorizationCheckerInterface $authChecker, ?RoleHierarchyInterface $roleHierarchy = null) - { - $this->expressionLanguage = $expressionLanguage; - $this->trustResolver = $trustResolver; - $this->authChecker = $authChecker; - $this->roleHierarchy = $roleHierarchy; + public function __construct( + private ExpressionLanguage $expressionLanguage, + private AuthenticationTrustResolverInterface $trustResolver, + private AuthorizationCheckerInterface $authChecker, + private ?RoleHierarchyInterface $roleHierarchy = null, + ) { } public function supportsAttribute(string $attribute): bool diff --git a/Authorization/Voter/RoleHierarchyVoter.php b/Authorization/Voter/RoleHierarchyVoter.php index 3535ca11..110faa03 100644 --- a/Authorization/Voter/RoleHierarchyVoter.php +++ b/Authorization/Voter/RoleHierarchyVoter.php @@ -22,12 +22,10 @@ */ class RoleHierarchyVoter extends RoleVoter { - private RoleHierarchyInterface $roleHierarchy; - - public function __construct(RoleHierarchyInterface $roleHierarchy, string $prefix = 'ROLE_') - { - $this->roleHierarchy = $roleHierarchy; - + public function __construct( + private RoleHierarchyInterface $roleHierarchy, + string $prefix = 'ROLE_', + ) { parent::__construct($prefix); } diff --git a/Authorization/Voter/RoleVoter.php b/Authorization/Voter/RoleVoter.php index 76de3a32..3c65fb63 100644 --- a/Authorization/Voter/RoleVoter.php +++ b/Authorization/Voter/RoleVoter.php @@ -20,11 +20,9 @@ */ class RoleVoter implements CacheableVoterInterface { - private string $prefix; - - public function __construct(string $prefix = 'ROLE_') - { - $this->prefix = $prefix; + public function __construct( + private string $prefix = 'ROLE_', + ) { } public function vote(TokenInterface $token, mixed $subject, array $attributes): int diff --git a/Authorization/Voter/TraceableVoter.php b/Authorization/Voter/TraceableVoter.php index 412bb976..1abc7c70 100644 --- a/Authorization/Voter/TraceableVoter.php +++ b/Authorization/Voter/TraceableVoter.php @@ -24,13 +24,10 @@ */ class TraceableVoter implements CacheableVoterInterface { - private VoterInterface $voter; - private EventDispatcherInterface $eventDispatcher; - - public function __construct(VoterInterface $voter, EventDispatcherInterface $eventDispatcher) - { - $this->voter = $voter; - $this->eventDispatcher = $eventDispatcher; + public function __construct( + private VoterInterface $voter, + private EventDispatcherInterface $eventDispatcher, + ) { } public function vote(TokenInterface $token, mixed $subject, array $attributes): int diff --git a/Event/AuthenticationEvent.php b/Event/AuthenticationEvent.php index 6fca50d4..f1683558 100644 --- a/Event/AuthenticationEvent.php +++ b/Event/AuthenticationEvent.php @@ -21,15 +21,13 @@ */ class AuthenticationEvent extends Event { - private TokenInterface $authenticationToken; - - public function __construct(TokenInterface $token) - { - $this->authenticationToken = $token; + public function __construct( + private TokenInterface $token, + ) { } public function getAuthenticationToken(): TokenInterface { - return $this->authenticationToken; + return $this->token; } } diff --git a/Event/VoteEvent.php b/Event/VoteEvent.php index 1b1d6a33..edc66b36 100644 --- a/Event/VoteEvent.php +++ b/Event/VoteEvent.php @@ -23,17 +23,12 @@ */ final class VoteEvent extends Event { - private VoterInterface $voter; - private mixed $subject; - private array $attributes; - private int $vote; - - public function __construct(VoterInterface $voter, mixed $subject, array $attributes, int $vote) - { - $this->voter = $voter; - $this->subject = $subject; - $this->attributes = $attributes; - $this->vote = $vote; + public function __construct( + private VoterInterface $voter, + private mixed $subject, + private array $attributes, + private int $vote, + ) { } public function getVoter(): VoterInterface diff --git a/Exception/LazyResponseException.php b/Exception/LazyResponseException.php index e26a3347..a354e68e 100644 --- a/Exception/LazyResponseException.php +++ b/Exception/LazyResponseException.php @@ -20,11 +20,9 @@ */ class LazyResponseException extends \Exception implements ExceptionInterface { - private Response $response; - - public function __construct(Response $response) - { - $this->response = $response; + public function __construct( + private Response $response, + ) { } public function getResponse(): Response diff --git a/Exception/TooManyLoginAttemptsAuthenticationException.php b/Exception/TooManyLoginAttemptsAuthenticationException.php index da1a1a7a..7bb74d64 100644 --- a/Exception/TooManyLoginAttemptsAuthenticationException.php +++ b/Exception/TooManyLoginAttemptsAuthenticationException.php @@ -19,11 +19,9 @@ */ class TooManyLoginAttemptsAuthenticationException extends AuthenticationException { - private ?int $threshold; - - public function __construct(?int $threshold = null) - { - $this->threshold = $threshold; + public function __construct( + private ?int $threshold = null, + ) { } public function getMessageData(): array diff --git a/Role/RoleHierarchy.php b/Role/RoleHierarchy.php index 15c5750d..a2a58457 100644 --- a/Role/RoleHierarchy.php +++ b/Role/RoleHierarchy.php @@ -21,15 +21,12 @@ class RoleHierarchy implements RoleHierarchyInterface /** @var array> */ protected array $map; - private array $hierarchy; - /** * @param array> $hierarchy */ - public function __construct(array $hierarchy) - { - $this->hierarchy = $hierarchy; - + public function __construct( + private array $hierarchy, + ) { $this->buildRoleMap(); } diff --git a/Signature/ExpiredSignatureStorage.php b/Signature/ExpiredSignatureStorage.php index 20803b97..62026644 100644 --- a/Signature/ExpiredSignatureStorage.php +++ b/Signature/ExpiredSignatureStorage.php @@ -18,13 +18,10 @@ */ final class ExpiredSignatureStorage { - private CacheItemPoolInterface $cache; - private int $lifetime; - - public function __construct(CacheItemPoolInterface $cache, int $lifetime) - { - $this->cache = $cache; - $this->lifetime = $lifetime; + public function __construct( + private CacheItemPoolInterface $cache, + private int $lifetime, + ) { } public function countUsages(string $hash): int diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index 3f86fce0..b38a449c 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -25,28 +25,21 @@ */ class SignatureHasher { - private PropertyAccessorInterface $propertyAccessor; - private array $signatureProperties; - private string $secret; - private ?ExpiredSignatureStorage $expiredSignaturesStorage; - private ?int $maxUses; - /** * @param array $signatureProperties Properties of the User; the hash is invalidated if these properties change * @param ExpiredSignatureStorage|null $expiredSignaturesStorage If provided, secures a sequence of hashes that are expired * @param int|null $maxUses Used together with $expiredSignatureStorage to allow a maximum usage of a hash */ - public function __construct(PropertyAccessorInterface $propertyAccessor, array $signatureProperties, #[\SensitiveParameter] string $secret, ?ExpiredSignatureStorage $expiredSignaturesStorage = null, ?int $maxUses = null) - { + public function __construct( + private PropertyAccessorInterface $propertyAccessor, + private array $signatureProperties, + #[\SensitiveParameter] private string $secret, + private ?ExpiredSignatureStorage $expiredSignaturesStorage = null, + private ?int $maxUses = null, + ) { if (!$secret) { throw new InvalidArgumentException('A non-empty secret is required.'); } - - $this->propertyAccessor = $propertyAccessor; - $this->signatureProperties = $signatureProperties; - $this->secret = $secret; - $this->expiredSignaturesStorage = $expiredSignaturesStorage; - $this->maxUses = $maxUses; } /** diff --git a/Test/AccessDecisionStrategyTestCase.php b/Test/AccessDecisionStrategyTestCase.php index bf2a2b9a..85e9fea8 100644 --- a/Test/AccessDecisionStrategyTestCase.php +++ b/Test/AccessDecisionStrategyTestCase.php @@ -64,11 +64,9 @@ final protected static function getVoters(int $grants, int $denies, int $abstain final protected static function getVoter(int $vote): VoterInterface { return new class($vote) implements VoterInterface { - private int $vote; - - public function __construct(int $vote) - { - $this->vote = $vote; + public function __construct( + private int $vote, + ) { } public function vote(TokenInterface $token, $subject, array $attributes): int diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index cef93a2d..f4329e88 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -26,14 +26,12 @@ */ class ChainUserProvider implements UserProviderInterface, PasswordUpgraderInterface { - private iterable $providers; - /** * @param iterable $providers */ - public function __construct(iterable $providers) - { - $this->providers = $providers; + public function __construct( + private iterable $providers, + ) { } /** diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index c319e1f9..5840d0bb 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -22,20 +22,18 @@ final class InMemoryUser implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface, \Stringable { private string $username; - private ?string $password; - private bool $enabled; - private array $roles; - public function __construct(?string $username, ?string $password, array $roles = [], bool $enabled = true) - { + public function __construct( + ?string $username, + private ?string $password, + private array $roles = [], + private bool $enabled = true, + ) { if ('' === $username || null === $username) { throw new \InvalidArgumentException('The username cannot be empty.'); } $this->username = $username; - $this->password = $password; - $this->enabled = $enabled; - $this->roles = $roles; } public function __toString(): string diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 3d6c7637..c3869824 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -22,13 +22,10 @@ class UserPasswordValidator extends ConstraintValidator { - private TokenStorageInterface $tokenStorage; - private PasswordHasherFactoryInterface $hasherFactory; - - public function __construct(TokenStorageInterface $tokenStorage, PasswordHasherFactoryInterface $hasherFactory) - { - $this->tokenStorage = $tokenStorage; - $this->hasherFactory = $hasherFactory; + public function __construct( + private TokenStorageInterface $tokenStorage, + private PasswordHasherFactoryInterface $hasherFactory, + ) { } public function validate(mixed $password, Constraint $constraint): void From e70f73a3c359bb09d77e624579f5d0282bb25f19 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 124/138] Prefix all sprintf() calls --- Authentication/Token/AbstractToken.php | 4 ++-- Authorization/AccessDecisionManager.php | 4 ++-- Authorization/ExpressionLanguage.php | 2 +- Authorization/ExpressionLanguageProvider.php | 2 +- Signature/SignatureHasher.php | 4 ++-- Tests/Resources/TranslationFilesTest.php | 4 ++-- User/ChainUserProvider.php | 6 +++--- User/InMemoryUserProvider.php | 6 +++--- User/MissingUserProvider.php | 2 +- Validator/Constraints/UserPasswordValidator.php | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Authentication/Token/AbstractToken.php b/Authentication/Token/AbstractToken.php index 36d64766..67d992ce 100644 --- a/Authentication/Token/AbstractToken.php +++ b/Authentication/Token/AbstractToken.php @@ -125,7 +125,7 @@ public function hasAttribute(string $name): bool public function getAttribute(string $name): mixed { if (!\array_key_exists($name, $this->attributes)) { - throw new \InvalidArgumentException(sprintf('This token has no "%s" attribute.', $name)); + throw new \InvalidArgumentException(\sprintf('This token has no "%s" attribute.', $name)); } return $this->attributes[$name]; @@ -146,7 +146,7 @@ public function __toString(): string $roles[] = $role; } - return sprintf('%s(user="%s", roles="%s")', $class, $this->getUserIdentifier(), implode(', ', $roles)); + return \sprintf('%s(user="%s", roles="%s")', $class, $this->getUserIdentifier(), implode(', ', $roles)); } /** diff --git a/Authorization/AccessDecisionManager.php b/Authorization/AccessDecisionManager.php index 10969acb..3e42c4bf 100644 --- a/Authorization/AccessDecisionManager.php +++ b/Authorization/AccessDecisionManager.php @@ -53,7 +53,7 @@ public function decide(TokenInterface $token, array $attributes, mixed $object = { // Special case for AccessListener, do not remove the right side of the condition before 6.0 if (\count($attributes) > 1 && !$allowMultipleAttributes) { - throw new InvalidArgumentException(sprintf('Passing more than one Security attribute to "%s()" is not supported.', __METHOD__)); + throw new InvalidArgumentException(\sprintf('Passing more than one Security attribute to "%s()" is not supported.', __METHOD__)); } return $this->strategy->decide( @@ -69,7 +69,7 @@ private function collectResults(TokenInterface $token, array $attributes, mixed foreach ($this->getVoters($attributes, $object) as $voter) { $result = $voter->vote($token, $object, $attributes); if (!\is_int($result) || !(self::VALID_VOTES[$result] ?? false)) { - throw new \LogicException(sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.', get_debug_type($voter), VoterInterface::class, var_export($result, true))); + throw new \LogicException(\sprintf('"%s::vote()" must return one of "%s" constants ("ACCESS_GRANTED", "ACCESS_DENIED" or "ACCESS_ABSTAIN"), "%s" returned.', get_debug_type($voter), VoterInterface::class, var_export($result, true))); } yield $result; diff --git a/Authorization/ExpressionLanguage.php b/Authorization/ExpressionLanguage.php index a48d8148..846d2cf6 100644 --- a/Authorization/ExpressionLanguage.php +++ b/Authorization/ExpressionLanguage.php @@ -15,7 +15,7 @@ use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; if (!class_exists(BaseExpressionLanguage::class)) { - throw new \LogicException(sprintf('The "%s" class requires the "ExpressionLanguage" component. Try running "composer require symfony/expression-language".', ExpressionLanguage::class)); + throw new \LogicException(\sprintf('The "%s" class requires the "ExpressionLanguage" component. Try running "composer require symfony/expression-language".', ExpressionLanguage::class)); } else { // Help opcache.preload discover always-needed symbols class_exists(ExpressionLanguageProvider::class); diff --git a/Authorization/ExpressionLanguageProvider.php b/Authorization/ExpressionLanguageProvider.php index d3e2dac0..2e558c21 100644 --- a/Authorization/ExpressionLanguageProvider.php +++ b/Authorization/ExpressionLanguageProvider.php @@ -28,7 +28,7 @@ public function getFunctions(): array new ExpressionFunction('is_fully_authenticated', fn () => '$token && $auth_checker->isGranted("IS_AUTHENTICATED_FULLY")', fn (array $variables) => $variables['token'] && $variables['auth_checker']->isGranted('IS_AUTHENTICATED_FULLY')), - new ExpressionFunction('is_granted', fn ($attributes, $object = 'null') => sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object), fn (array $variables, $attributes, $object = null) => $variables['auth_checker']->isGranted($attributes, $object)), + new ExpressionFunction('is_granted', fn ($attributes, $object = 'null') => \sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object), fn (array $variables, $attributes, $object = null) => $variables['auth_checker']->isGranted($attributes, $object)), new ExpressionFunction('is_remember_me', fn () => '$token && $auth_checker->isGranted("IS_REMEMBERED")', fn (array $variables) => $variables['token'] && $variables['auth_checker']->isGranted('IS_REMEMBERED')), ]; diff --git a/Signature/SignatureHasher.php b/Signature/SignatureHasher.php index b38a449c..903f5b34 100644 --- a/Signature/SignatureHasher.php +++ b/Signature/SignatureHasher.php @@ -87,7 +87,7 @@ public function verifySignatureHash(UserInterface $user, int $expires, string $h if ($this->expiredSignaturesStorage && $this->maxUses) { if ($this->expiredSignaturesStorage->countUsages($hash) >= $this->maxUses) { - throw new ExpiredSignatureException(sprintf('Signature can only be used "%d" times.', $this->maxUses)); + throw new ExpiredSignatureException(\sprintf('Signature can only be used "%d" times.', $this->maxUses)); } $this->expiredSignaturesStorage->incrementUsages($hash); @@ -111,7 +111,7 @@ public function computeSignatureHash(UserInterface $user, int $expires): string } if (!\is_scalar($value) && !$value instanceof \Stringable) { - throw new \InvalidArgumentException(sprintf('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned.', $property, $user::class, get_debug_type($value))); + throw new \InvalidArgumentException(\sprintf('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned.', $property, $user::class, get_debug_type($value))); } hash_update($fieldsHash, ':'.base64_encode($value)); } diff --git a/Tests/Resources/TranslationFilesTest.php b/Tests/Resources/TranslationFilesTest.php index 6bd9a21d..695cdd98 100644 --- a/Tests/Resources/TranslationFilesTest.php +++ b/Tests/Resources/TranslationFilesTest.php @@ -26,7 +26,7 @@ public function testTranslationFileIsValid($filePath) $errors = XliffUtils::validateSchema($document); - $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } /** @@ -39,7 +39,7 @@ public function testTranslationFileIsValidWithoutEntityLoader($filePath) $errors = XliffUtils::validateSchema($document); - $this->assertCount(0, $errors, sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); + $this->assertCount(0, $errors, \sprintf('"%s" is invalid:%s', $filePath, \PHP_EOL.implode(\PHP_EOL, array_column($errors, 'message')))); } public static function provideTranslationFiles() diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index f4329e88..484d0d47 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -56,7 +56,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface } } - $ex = new UserNotFoundException(sprintf('There is no user with identifier "%s".', $identifier)); + $ex = new UserNotFoundException(\sprintf('There is no user with identifier "%s".', $identifier)); $ex->setUserIdentifier($identifier); throw $ex; } @@ -82,11 +82,11 @@ public function refreshUser(UserInterface $user): UserInterface if ($supportedUserFound) { $username = $user->getUserIdentifier(); - $e = new UserNotFoundException(sprintf('There is no user with name "%s".', $username)); + $e = new UserNotFoundException(\sprintf('There is no user with name "%s".', $username)); $e->setUserIdentifier($username); throw $e; } else { - throw new UnsupportedUserException(sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', get_debug_type($user))); + throw new UnsupportedUserException(\sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', get_debug_type($user))); } } diff --git a/User/InMemoryUserProvider.php b/User/InMemoryUserProvider.php index 04bf682a..db6267a5 100644 --- a/User/InMemoryUserProvider.php +++ b/User/InMemoryUserProvider.php @@ -55,7 +55,7 @@ public function __construct(array $users = []) public function createUser(UserInterface $user): void { if (!$user instanceof InMemoryUser) { - throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); + throw new UnsupportedUserException(\sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } $userIdentifier = strtolower($user->getUserIdentifier()); @@ -76,7 +76,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface public function refreshUser(UserInterface $user): UserInterface { if (!$user instanceof InMemoryUser) { - throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_debug_type($user))); + throw new UnsupportedUserException(\sprintf('Instances of "%s" are not supported.', get_debug_type($user))); } $storedUser = $this->getUser($user->getUserIdentifier()); @@ -98,7 +98,7 @@ public function supportsClass(string $class): bool private function getUser(string $username): InMemoryUser { if (!isset($this->users[strtolower($username)])) { - $ex = new UserNotFoundException(sprintf('Username "%s" does not exist.', $username)); + $ex = new UserNotFoundException(\sprintf('Username "%s" does not exist.', $username)); $ex->setUserIdentifier($username); throw $ex; diff --git a/User/MissingUserProvider.php b/User/MissingUserProvider.php index cf6102aa..9869259a 100644 --- a/User/MissingUserProvider.php +++ b/User/MissingUserProvider.php @@ -28,7 +28,7 @@ class MissingUserProvider implements UserProviderInterface */ public function __construct(string $firewall) { - throw new InvalidConfigurationException(sprintf('"%s" firewall requires a user provider but none was defined.', $firewall)); + throw new InvalidConfigurationException(\sprintf('"%s" firewall requires a user provider but none was defined.', $firewall)); } public function loadUserByUsername(string $username): UserInterface diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index c3869824..6c9bdef9 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -49,7 +49,7 @@ public function validate(mixed $password, Constraint $constraint): void $user = $this->tokenStorage->getToken()->getUser(); if (!$user instanceof PasswordAuthenticatedUserInterface) { - throw new ConstraintDefinitionException(sprintf('The "%s" class must implement the "%s" interface.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); + throw new ConstraintDefinitionException(\sprintf('The "%s" class must implement the "%s" interface.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); } $hasher = $this->hasherFactory->getPasswordHasher($user); From 40cbdcaad146cd431686dddaf5231f13579f1d00 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 125/138] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 84c7add0..14c3c359 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore From 138cfe23ea2e991f3c9999c077779dcdce03e0f6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 19 Jul 2024 09:42:17 +0200 Subject: [PATCH 126/138] pass the current token to the checkPostAuth() method of user checkers --- CHANGELOG.md | 1 + User/ChainUserChecker.php | 12 ++++++++++-- User/UserCheckerInterface.php | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 208f0d48..ac99a3c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.2 --- + * Add `$token` argument to `UserCheckerInterface::checkPostAuth()` * Deprecate argument `$secret` of `RememberMeToken` 7.0 diff --git a/User/ChainUserChecker.php b/User/ChainUserChecker.php index f889d35d..67fd76b9 100644 --- a/User/ChainUserChecker.php +++ b/User/ChainUserChecker.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\User; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + final class ChainUserChecker implements UserCheckerInterface { /** @@ -27,10 +29,16 @@ public function checkPreAuth(UserInterface $user): void } } - public function checkPostAuth(UserInterface $user): void + public function checkPostAuth(UserInterface $user /*, TokenInterface $token*/): void { + $token = 1 < \func_num_args() ? func_get_arg(1) : null; + foreach ($this->checkers as $checker) { - $checker->checkPostAuth($user); + if ($token instanceof TokenInterface) { + $checker->checkPostAuth($user, $token); + } else { + $checker->checkPostAuth($user); + } } } } diff --git a/User/UserCheckerInterface.php b/User/UserCheckerInterface.php index 480ba7b5..2dc748aa 100644 --- a/User/UserCheckerInterface.php +++ b/User/UserCheckerInterface.php @@ -35,5 +35,5 @@ public function checkPreAuth(UserInterface $user): void; * * @throws AccountStatusException */ - public function checkPostAuth(UserInterface $user): void; + public function checkPostAuth(UserInterface $user /*, TokenInterface $token*/): void; } From 9b1348ac4bc9f25b6d9ccfd7e5e6b3af1cf78c0b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 31 Jul 2024 16:13:26 +0200 Subject: [PATCH 127/138] Remove unused code and unnecessary `else` branches --- User/ChainUserProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/User/ChainUserProvider.php b/User/ChainUserProvider.php index 484d0d47..21e2e618 100644 --- a/User/ChainUserProvider.php +++ b/User/ChainUserProvider.php @@ -85,9 +85,9 @@ public function refreshUser(UserInterface $user): UserInterface $e = new UserNotFoundException(\sprintf('There is no user with name "%s".', $username)); $e->setUserIdentifier($username); throw $e; - } else { - throw new UnsupportedUserException(\sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', get_debug_type($user))); } + + throw new UnsupportedUserException(\sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', get_debug_type($user))); } public function supportsClass(string $class): bool From f0267ab90e6edb18ec982ad481c867c7fb3375a9 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Thu, 1 Aug 2024 17:21:17 +0200 Subject: [PATCH 128/138] Code style change in `@PER-CS2.0` affecting `@Symfony` (parentheses for anonymous classes) --- Tests/Authorization/AccessDecisionManagerTest.php | 2 +- Tests/Authorization/Voter/AuthenticatedVoterTest.php | 2 +- Tests/Authorization/Voter/VoterTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Authorization/AccessDecisionManagerTest.php b/Tests/Authorization/AccessDecisionManagerTest.php index b06a7a79..f0a49788 100644 --- a/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Tests/Authorization/AccessDecisionManagerTest.php @@ -39,7 +39,7 @@ public function testVoterCalls() $this->getUnexpectedVoter(), ]; - $strategy = new class() implements AccessDecisionStrategyInterface { + $strategy = new class implements AccessDecisionStrategyInterface { public function decide(\Traversable $results): bool { $i = 0; diff --git a/Tests/Authorization/Voter/AuthenticatedVoterTest.php b/Tests/Authorization/Voter/AuthenticatedVoterTest.php index 3a3b9d4e..ed894b3a 100644 --- a/Tests/Authorization/Voter/AuthenticatedVoterTest.php +++ b/Tests/Authorization/Voter/AuthenticatedVoterTest.php @@ -90,7 +90,7 @@ protected function getToken($authenticated) $user = new InMemoryUser('wouter', '', ['ROLE_USER']); if ('fully' === $authenticated) { - $token = new class() extends AbstractToken { + $token = new class extends AbstractToken { public function getCredentials() { } diff --git a/Tests/Authorization/Voter/VoterTest.php b/Tests/Authorization/Voter/VoterTest.php index 5636340e..602c61ab 100644 --- a/Tests/Authorization/Voter/VoterTest.php +++ b/Tests/Authorization/Voter/VoterTest.php @@ -41,7 +41,7 @@ public static function getTests(): array [$voter, ['DELETE'], VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported'], - [$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, new class() {}, 'ACCESS_ABSTAIN if class is not supported'], + [$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, new class {}, 'ACCESS_ABSTAIN if class is not supported'], [$voter, ['EDIT'], VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null'], From a32dd8b58706d9d5be2ceeb2873276d600235a88 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 29 Jul 2024 09:33:48 +0200 Subject: [PATCH 129/138] Remove useless code --- User/InMemoryUser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/User/InMemoryUser.php b/User/InMemoryUser.php index 5840d0bb..b14bc077 100644 --- a/User/InMemoryUser.php +++ b/User/InMemoryUser.php @@ -88,8 +88,8 @@ public function isEqualTo(UserInterface $user): bool return false; } - $currentRoles = array_map('strval', (array) $this->getRoles()); - $newRoles = array_map('strval', (array) $user->getRoles()); + $currentRoles = array_map('strval', $this->getRoles()); + $newRoles = array_map('strval', $user->getRoles()); $rolesChanged = \count($currentRoles) !== \count($newRoles) || \count($currentRoles) !== \count(array_intersect($currentRoles, $newRoles)); if ($rolesChanged) { return false; From 0d94fd9669fe990d23d7d27ecb1e8448a025870c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20J=2E=20Garc=C3=ADa=20Lagar?= Date: Wed, 14 Aug 2024 08:46:31 +0200 Subject: [PATCH 130/138] Deprecate empty user identifier --- CHANGELOG.md | 1 + User/UserInterface.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac99a3c0..5dd5ef38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `$token` argument to `UserCheckerInterface::checkPostAuth()` * Deprecate argument `$secret` of `RememberMeToken` + * Deprecate returning an empty string in `UserInterface::getUserIdentifier()` 7.0 --- diff --git a/User/UserInterface.php b/User/UserInterface.php index 50f8fb0f..e6078399 100644 --- a/User/UserInterface.php +++ b/User/UserInterface.php @@ -56,6 +56,8 @@ public function eraseCredentials(): void; /** * Returns the identifier for this user (e.g. username or email address). + * + * @return non-empty-string */ public function getUserIdentifier(): string; } From 2acef5cfd7cc6697d4618cdd9eb363eb5ab713b2 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Aug 2024 17:35:30 +0200 Subject: [PATCH 131/138] Use Stringable whenever possible --- Authorization/TraceableAccessDecisionManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authorization/TraceableAccessDecisionManager.php b/Authorization/TraceableAccessDecisionManager.php index 17db5461..0b82eb3a 100644 --- a/Authorization/TraceableAccessDecisionManager.php +++ b/Authorization/TraceableAccessDecisionManager.php @@ -85,7 +85,7 @@ public function getStrategy(): string if (null === $this->strategy) { return '-'; } - if (method_exists($this->strategy, '__toString')) { + if ($this->strategy instanceof \Stringable) { return (string) $this->strategy; } From c563eafb17f8332faa9d600a82b7d732f22d3ae6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 5 Sep 2024 11:40:18 +0200 Subject: [PATCH 132/138] make test case classes compatible with PHPUnit 10+ --- CHANGELOG.md | 1 + Test/AccessDecisionStrategyTestCase.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dd5ef38..7cf09c70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.2 --- + * Make `AccessDecisionStrategyTestCase` compatible with PHPUnit 10+ * Add `$token` argument to `UserCheckerInterface::checkPostAuth()` * Deprecate argument `$secret` of `RememberMeToken` * Deprecate returning an empty string in `UserInterface::getUserIdentifier()` diff --git a/Test/AccessDecisionStrategyTestCase.php b/Test/AccessDecisionStrategyTestCase.php index 85e9fea8..792e7779 100644 --- a/Test/AccessDecisionStrategyTestCase.php +++ b/Test/AccessDecisionStrategyTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Core\Test; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; @@ -29,6 +30,7 @@ abstract class AccessDecisionStrategyTestCase extends TestCase * * @param VoterInterface[] $voters */ + #[DataProvider('provideStrategyTests')] final public function testDecide(AccessDecisionStrategyInterface $strategy, array $voters, bool $expected) { $token = $this->createMock(TokenInterface::class); From 8c7e52155262b3ef6b7885f6d9bd90fb24eaa66f Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 19 Sep 2024 23:53:46 +0200 Subject: [PATCH 133/138] Fix named arguments in data providers --- Tests/Authorization/TraceableAccessDecisionManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Authorization/TraceableAccessDecisionManagerTest.php b/Tests/Authorization/TraceableAccessDecisionManagerTest.php index cefe8dbc..468a89fc 100644 --- a/Tests/Authorization/TraceableAccessDecisionManagerTest.php +++ b/Tests/Authorization/TraceableAccessDecisionManagerTest.php @@ -116,7 +116,7 @@ public static function provideObjectsAndLogs(): \Generator 'result' => true, 'voterDetails' => [], ]], - 'attributes' => [12], + [12], 12345, [], true, From 611bc1065c6aa9d2a3b2c8cda28d56b5ef23a187 Mon Sep 17 00:00:00 2001 From: Link1515 Date: Thu, 2 Jan 2025 09:32:03 +0800 Subject: [PATCH 134/138] fix: modify Exception message parameter order --- Validator/Constraints/UserPasswordValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Validator/Constraints/UserPasswordValidator.php b/Validator/Constraints/UserPasswordValidator.php index 41670b27..79c7bd30 100644 --- a/Validator/Constraints/UserPasswordValidator.php +++ b/Validator/Constraints/UserPasswordValidator.php @@ -55,7 +55,7 @@ public function validate(mixed $password, Constraint $constraint) $user = $this->tokenStorage->getToken()->getUser(); if (!$user instanceof PasswordAuthenticatedUserInterface) { - throw new ConstraintDefinitionException(sprintf('The "%s" class must implement the "%s" interface.', PasswordAuthenticatedUserInterface::class, get_debug_type($user))); + throw new ConstraintDefinitionException(sprintf('The "%s" class must implement the "%s" interface.', get_debug_type($user), PasswordAuthenticatedUserInterface::class)); } $hasher = $this->hasherFactory->getPasswordHasher($user); From 7e201a5edb661c9f61b5eb20bdb48e6015903ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDan=20V=2E=20Dragan?= Date: Thu, 16 Jan 2025 12:58:10 +0100 Subject: [PATCH 135/138] [Security][Validators] Review translations. --- Resources/translations/security.sl.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/translations/security.sl.xlf b/Resources/translations/security.sl.xlf index 7d051400..2b7a592b 100644 --- a/Resources/translations/security.sl.xlf +++ b/Resources/translations/security.sl.xlf @@ -76,7 +76,7 @@ Too many failed login attempts, please try again in %minutes% minutes. - Preveč neuspešnih poskusov prijave, poskusite znova čez %minutes% minuto.|Preveč neuspešnih poskusov prijave, poskusite znova čez %minutes% minut. + Preveč neuspešnih poskusov prijave, poskusite znova čez %minutes% minuto.|Preveč neuspešnih poskusov prijave, poskusite znova čez %minutes% minuti.|Preveč neuspešnih poskusov prijave, poskusite znova čez %minutes% minute.|Preveč neuspešnih poskusov prijave, poskusite znova čez %minutes% minut. From df18ec8c5d3a5f347c3a1d4aa59b787ae676e123 Mon Sep 17 00:00:00 2001 From: tito10047 Date: Fri, 17 Jan 2025 08:48:36 +0100 Subject: [PATCH 136/138] Fix #53778 --- Resources/translations/security.sk.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/translations/security.sk.xlf b/Resources/translations/security.sk.xlf index b08757de..3820bdcc 100644 --- a/Resources/translations/security.sk.xlf +++ b/Resources/translations/security.sk.xlf @@ -76,7 +76,7 @@ Too many failed login attempts, please try again in %minutes% minutes. - Príliš veľa neúspešných pokusov o prihlásenie, skúste to prosím znova o %minutes% minútu.|Príliš veľa neúspešných pokusov o prihlásenie, skúste to prosím znova o %minutes% minúty.|Príliš veľa neúspešných pokusov o prihlásenie, skúste to prosím znova o %minutes% minút. + Príliš veľa neúspešných pokusov o prihlásenie, skúste to prosím znova o %minutes% minútu.|Príliš veľa neúspešných pokusov o prihlásenie, skúste to prosím znova o %minutes% minúty.|Príliš veľa neúspešných pokusov o prihlásenie, skúste to prosím znova o %minutes% minút. From 0ae7ae716968e00287ab9b7768405e0dc9cad109 Mon Sep 17 00:00:00 2001 From: Gil Hadad Date: Wed, 22 Jan 2025 22:59:03 +0200 Subject: [PATCH 137/138] Fixed mistakes in proper hebrew writing in the previous translation and confirmed the rest to be correct and in the same style. --- Resources/translations/security.he.xlf | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Resources/translations/security.he.xlf b/Resources/translations/security.he.xlf index b1d6afd4..1cf02a4e 100644 --- a/Resources/translations/security.he.xlf +++ b/Resources/translations/security.he.xlf @@ -4,15 +4,15 @@ An authentication exception occurred. - שגיאה באימות + התרחשה שגיאה באימות. Authentication credentials could not be found. - פרטי זיהוי לא נמצאו. + פרטי הזיהוי לא נמצאו. Authentication request could not be processed due to a system problem. - לא ניתן היה לעבד את בקשת אימות בגלל בעיית מערכת. + לא ניתן היה לעבד את בקשת האימות בגלל בעיית מערכת. Invalid credentials. @@ -20,7 +20,7 @@ Cookie has already been used by someone else. - עוגיה כבר שומשה. + עוגיה כבר שומשה על ידי מישהו אחר. Not privileged to request the resource. @@ -32,15 +32,15 @@ No authentication provider found to support the authentication token. - לא נמצא ספק אימות המתאימה לבקשה. + לא נמצא ספק אימות המתאים לבקשה. No session available, it either timed out or cookies are not enabled. - אין סיישן זמין, או שתם הזמן הקצוב או העוגיות אינן מופעלות. + אין מפגש זמין, תם הזמן הקצוב או שהעוגיות אינן מופעלות. No token could be found. - הטוקן לא נמצא. + אסימון לא נמצא. Username could not be found. @@ -72,11 +72,11 @@ Too many failed login attempts, please try again in %minutes% minute. - יותר מדי ניסיונות כניסה כושלים, אנא נסה שוב בוד %minutes% דקה. + יותר מדי ניסיונות כניסה כושלים, אנא נסה שוב בעוד %minutes% דקה. Too many failed login attempts, please try again in %minutes% minutes. - יותר מדי ניסיונות כניסה כושלים, אנא נסה שוב בעוד %minutes% דקות. + יותר מדי ניסיונות כניסה כושלים, אנא נסה שוב בעוד %minutes% דקות. From c6e70da38436a9a49ed39d9cbead1ecf760f0fbd Mon Sep 17 00:00:00 2001 From: John Edmerson Pizarra Date: Thu, 17 Apr 2025 15:43:34 +0800 Subject: [PATCH 138/138] Add Tagalog translations for security and validator components --- Resources/translations/security.tl.xlf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/translations/security.tl.xlf b/Resources/translations/security.tl.xlf index c02222de..aa47f179 100644 --- a/Resources/translations/security.tl.xlf +++ b/Resources/translations/security.tl.xlf @@ -72,11 +72,11 @@ Too many failed login attempts, please try again in %minutes% minute. - Napakaraming nabigong mga pagtatangka sa pag-login, pakisubukan ulit sa% minuto% minuto. + Napakaraming nabigong mga pagtatangka sa pag-login, pakisubukan ulit matapos ang %minutes% minuto. Too many failed login attempts, please try again in %minutes% minutes. - Napakaraming nabigong pagtatangka ng pag-login, mangyaring subukang muli sa loob ng %minutes% minuto.|Napakaraming nabigong pagtatangka ng pag-login, mangyaring subukang muli sa loob ng %minutes% minuto. + Napakaraming nabigong mga pagtatangka sa pag-login, pakisubukan ulit matapos ang %minutes% minuto.