diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 00d0053dc4cbd..5f5a3beceb60b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -835,9 +835,18 @@ private function addValidationSection(ArrayNodeDefinition $rootNode) ->end() ->end() ->end() - ->booleanNode('disable_not_compromised_password') - ->defaultFalse() - ->info('Disable NotCompromisedPassword Validator: the value will always be valid.') + ->arrayNode('not_compromised_password') + ->canBeDisabled() + ->children() + ->booleanNode('enabled') + ->defaultTrue() + ->info('When disabled, compromised passwords will be accepted as valid.') + ->end() + ->scalarNode('endpoint') + ->defaultNull() + ->info('API endpoint for the NotCompromisedPassword Validator.') + ->end() + ->end() ->end() ->arrayNode('auto_mapping') ->useAttributeAsKey('namespace') diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 338eddb949cbc..26f2b7ece0727 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1249,7 +1249,8 @@ private function registerValidationConfiguration(array $config, ContainerBuilder $container ->getDefinition('validator.not_compromised_password') - ->setArgument(2, $config['disable_not_compromised_password']) + ->setArgument(2, $config['not_compromised_password']['enabled']) + ->setArgument(3, $config['not_compromised_password']['endpoint']) ; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 827988669fb8d..2dcc5b84a7746 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -235,7 +235,10 @@ protected static function getBundleDefaultConfig() 'paths' => [], ], 'auto_mapping' => [], - 'disable_not_compromised_password' => false, + 'not_compromised_password' => [ + 'enabled' => true, + 'endpoint' => null, + ], ], 'annotations' => [ 'cache' => 'php_array', diff --git a/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php b/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php index 20ecdd381a100..7b8dd423532ba 100644 --- a/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php +++ b/src/Symfony/Component/Validator/Constraints/NotCompromisedPasswordValidator.php @@ -29,13 +29,14 @@ */ class NotCompromisedPasswordValidator extends ConstraintValidator { - private const RANGE_API = 'https://api.pwnedpasswords.com/range/%s'; + private const DEFAULT_API_ENDPOINT = 'https://api.pwnedpasswords.com/range/%s'; private $httpClient; private $charset; - private $disabled; + private $enabled; + private $endpoint; - public function __construct(HttpClientInterface $httpClient = null, string $charset = 'UTF-8', bool $disabled = false) + public function __construct(HttpClientInterface $httpClient = null, string $charset = 'UTF-8', bool $enabled = true, string $endpoint = null) { if (null === $httpClient && !class_exists(HttpClient::class)) { throw new \LogicException(sprintf('The "%s" class requires the "HttpClient" component. Try running "composer require symfony/http-client".', self::class)); @@ -43,7 +44,8 @@ public function __construct(HttpClientInterface $httpClient = null, string $char $this->httpClient = $httpClient ?? HttpClient::create(); $this->charset = $charset; - $this->disabled = $disabled; + $this->enabled = $enabled; + $this->endpoint = $endpoint ?? self::DEFAULT_API_ENDPOINT; } /** @@ -57,7 +59,7 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($constraint, NotCompromisedPassword::class); } - if ($this->disabled) { + if (!$this->enabled) { return; } @@ -76,7 +78,7 @@ public function validate($value, Constraint $constraint) $hash = strtoupper(sha1($value)); $hashPrefix = substr($hash, 0, 5); - $url = sprintf(self::RANGE_API, $hashPrefix); + $url = sprintf($this->endpoint, $hashPrefix); try { $result = $this->httpClient->request('GET', $url)->getContent(); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NotCompromisedPasswordValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NotCompromisedPasswordValidatorTest.php index 8f0ecd2ecf5c2..5dab9be108d33 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/NotCompromisedPasswordValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/NotCompromisedPasswordValidatorTest.php @@ -62,9 +62,9 @@ public function testEmptyStringIsValid() public function testInvalidPasswordButDisabled() { - $r = new \ReflectionProperty($this->validator, 'disabled'); + $r = new \ReflectionProperty($this->validator, 'enabled'); $r->setAccessible(true); - $r->setValue($this->validator, true); + $r->setValue($this->validator, false); $this->validator->validate(self::PASSWORD_LEAKED, new NotCompromisedPassword()); @@ -128,6 +128,29 @@ public function testNonUtf8CharsetInvalid() ->assertRaised(); } + public function testInvalidPasswordCustomEndpoint() + { + $endpoint = 'https://password-check.internal.example.com/range/%s'; + // 50D74 - first 5 bytes of uppercase SHA1 hash of self::PASSWORD_LEAKED + $expectedEndpointUrl = 'https://password-check.internal.example.com/range/50D74'; + $constraint = new NotCompromisedPassword(); + + $this->context = $this->createContext(); + + $validator = new NotCompromisedPasswordValidator( + $this->createHttpClientStubCustomEndpoint($expectedEndpointUrl), + 'UTF-8', + true, + $endpoint + ); + $validator->initialize($this->context); + $validator->validate(self::PASSWORD_LEAKED, $constraint); + + $this->buildViolation($constraint->message) + ->setCode(NotCompromisedPassword::COMPROMISED_PASSWORD_ERROR) + ->assertRaised(); + } + /** * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException */ @@ -184,4 +207,21 @@ public function getResponse(): ResponseInterface return $httpClientStub; } + + private function createHttpClientStubCustomEndpoint($expectedEndpoint): HttpClientInterface + { + $httpClientStub = $this->createMock(HttpClientInterface::class); + $httpClientStub->method('request')->with('GET', $expectedEndpoint)->will( + $this->returnCallback(function (string $method, string $url): ResponseInterface { + $responseStub = $this->createMock(ResponseInterface::class); + $responseStub + ->method('getContent') + ->willReturn(implode("\r\n", self::RETURN)); + + return $responseStub; + }) + ); + + return $httpClientStub; + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy