8000 [Validator] Make API endpoint for NotCompromisedPasswordValidator configurable by xelan · Pull Request #31060 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Validator] Make API endpoint for NotCompromisedPasswordValidator configurable #31060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,23 @@
*/
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));
}

$this->httpClient = $httpClient ?? HttpClient::create();
$this->charset = $charset;
$this->disabled = $disabled;
$this->enabled = $enabled;
$this->endpoint = $endpoint ?? self::DEFAULT_API_ENDPOINT;
}

/**
Expand All @@ -57,7 +59,7 @@ public function validate($value, Constraint $constraint)
throw new UnexpectedTypeException($constraint, NotCompromisedPassword::class);
}

if ($this->disabled) {
if (!$this->enabled) {
return;
}

Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
}
}
0