From f6a80c214da9a38f10a01359d4d34ef9e5fae79e Mon Sep 17 00:00:00 2001 From: Andreas Erhard Date: Wed, 10 Apr 2019 15:29:36 +0200 Subject: [PATCH] [Validator] Make API endpoint for NotCompromisedPasswordValidator configurable --- .../DependencyInjection/Configuration.php | 15 +++++-- .../FrameworkExtension.php | 3 +- .../DependencyInjection/ConfigurationTest.php | 5 ++- .../NotCompromisedPasswordValidator.php | 14 +++--- .../NotCompromisedPasswordValidatorTest.php | 44 ++++++++++++++++++- 5 files changed, 68 insertions(+), 13 deletions(-) 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