diff --git a/lib/PasswordValidator.php b/lib/PasswordValidator.php index c5293e9a..d4a546dd 100644 --- a/lib/PasswordValidator.php +++ b/lib/PasswordValidator.php @@ -25,33 +25,27 @@ use OC\HintException; -use OCP\Http\Client\IClientService; -use OCP\IL10N; +use OCA\Password_Policy\Validator\CommonPasswordsValidator; +use OCA\Password_Policy\Validator\HIBPValidator; +use OCA\Password_Policy\Validator\IValidator; +use OCA\Password_Policy\Validator\LengthValidator; +use OCA\Password_Policy\Validator\NumericCharacterValidator; +use OCA\Password_Policy\Validator\SpecialCharactersValidator; +use OCA\Password_Policy\Validator\UpperCaseLoweCaseValidator; +use OCP\AppFramework\IAppContainer; +use OCP\AppFramework\QueryException; +use OCP\ILogger; class PasswordValidator { - /** @var PasswordPolicyConfig */ - private $config; + /** @var IAppContainer */ + private $container; + /** @var ILogger */ + private $logger; - /** @var IL10N */ - private $l; - - /** @var IClientService */ - private $clientService; - - /** - * PasswordValidator constructor. - * - * @param PasswordPolicyConfig $config - * @param IL10N $l - * @param IClientService $clientService - */ - public function __construct(PasswordPolicyConfig $config, - IL10N $l, - IClientService $clientService) { - $this->config = $config; - $this->l = $l; - $this->clientService = $clientService; + public function __construct(IAppContainer $container, ILogger $logger) { + $this->container = $container; + $this->logger = $logger; } /** @@ -60,146 +54,27 @@ public function __construct(PasswordPolicyConfig $config, * @param string $password * @throws HintException */ - public function validate($password) { - $this->checkCommonPasswords($password); - $this->checkPasswordLength($password); - $this->checkNumericCharacters($password); - $this->checkUpperLowerCase($password); - $this->checkSpecialCharacters($password); - $this->checkHaveIBeenPwned($password); - } - - /** - * check if password matches the minimum length defined by the admin - * - * @param string $password - * @throws HintException - */ - protected function checkPasswordLength($password) { - $minLength = $this->config->getMinLength(); - if(strlen($password) < $minLength) { - $message = 'Password needs to be at least ' . $minLength . ' characters long'; - $message_t = $this->l->t( - 'Password needs to be at least %s characters long', [$minLength] - ); - throw new HintException($message, $message_t); - } - } - - /** - * check if password contain at least one upper and one lower case character - * - * @param string $password - * @throws HintException - */ - protected function checkUpperLowerCase($password) { - $enforceUpperLowerCase = $this->config->getEnforceUpperLowerCase(); - if($enforceUpperLowerCase) { - if (preg_match('/^(?=.*[a-z])(?=.*[A-Z]).+$/', $password) !== 1) { - $message = 'Password needs to contain at least one lower and one upper case character.'; - $message_t = $this->l->t( - 'Password needs to contain at least one lower and one upper case character.' - ); - throw new HintException($message, $message_t); - } - } - } - - /** - * check if password contain at least one numeric character - * - * @param string $password - * @throws HintException - */ - protected function checkNumericCharacters($password) { - $enforceNumericCharacters = $this->config->getEnforceNumericCharacters(); - if($enforceNumericCharacters) { - if (preg_match('/^(?=.*\d).+$/', $password) !== 1) { - $message = 'Password needs to contain at least one numeric character'; - $message_t = $this->l->t( - 'Password needs to contain at least one numeric character.' - ); - throw new HintException($message, $message_t); - } - } - } - - /** - * check if password contain at least one special character - * - * @param string $password - * @throws HintException - */ - protected function checkSpecialCharacters($password) { - $enforceSpecialCharacters = $this->config->getEnforceSpecialCharacters(); - if($enforceSpecialCharacters && ctype_alnum($password)) { - $message = 'Password needs to contain at least one special character.'; - $message_t = $this->l->t( - 'Password needs to contain at least one special character.' - ); - throw new HintException($message, $message_t); - } - } - - - /** - * Checks if password is within the 100,000 most used passwords. - * - * @param string $password - * @throws HintException - */ - protected function checkCommonPasswords($password) { - $enforceNonCommonPassword = $this->config->getEnforceNonCommonPassword(); - if($enforceNonCommonPassword) { - $passwordFile = __DIR__ . '/../lists/list-'.strlen($password).'.php'; - if(file_exists($passwordFile)) { - $commonPasswords = require_once $passwordFile; - if (isset($commonPasswords[strtolower($password)])) { - $message = 'Password is among the 1,000,000 most common ones. Please make it unique.'; - $message_t = $this->l->t( - 'Password is among the 1,000,000 most common ones. Please make it unique.' - ); - throw new HintException($message, $message_t); - } - } - } - } - - /** - * Check if a password is in the list of breached passwords from - * haveibeenpwned.com - * - * @param string $password - * @throws HintException - */ - protected function checkHaveIBeenPwned($password) { - if ($this->config->getEnforceHaveIBeenPwned()) { - $hash = sha1($password); - $range = substr($hash, 0, 5); - $needle = strtoupper(substr($hash, 5)); - - $client = $this->clientService->newClient(); - + public function validate(string $password): void { + $validators = [ + CommonPasswordsValidator::class, + LengthValidator::class, + NumericCharacterValidator::class, + UpperCaseLoweCaseValidator::class, + SpecialCharactersValidator::class, + HIBPValidator::class, + ]; + + foreach ($validators as $validator) { try { - $response = $client->get( - 'https://api.pwnedpasswords.com/range/' . $range, - [ - 'timeout' => 5 - ] - ); - } catch (\Exception $e) { - return; + /** @var IValidator $instance */ + $instance = $this->container->query($validator); + } catch (QueryException $e) { + //ignore and continue + $this->logger->logException($e, ['level' => ILogger::INFO]); + continue; } - $result = $response->getBody(); - - if (strpos($result, $needle) !== false) { - $message = 'Password is present in compromised password list. Please choose a different password.'; - $message_t = $this->l->t( - 'Password is present in compromised password list. Please choose a different password.' - ); - throw new HintException($message, $message_t); - } + $instance->validate($password); } } diff --git a/lib/Validator/CommonPasswordsValidator.php b/lib/Validator/CommonPasswordsValidator.php new file mode 100644 index 00000000..f0942bd8 --- /dev/null +++ b/lib/Validator/CommonPasswordsValidator.php @@ -0,0 +1,58 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCP\IL10N; + +class CommonPasswordsValidator implements IValidator { + + /** @var PasswordPolicyConfig */ + private $config; + /** @var IL10N */ + private $l; + + public function __construct(PasswordPolicyConfig $config, IL10N $l) { + $this->config = $config; + $this->l = $l; + } + + public function validate(string $password): void { + $enforceNonCommonPassword = $this->config->getEnforceNonCommonPassword(); + $passwordFile = __DIR__ . '/../../lists/list-'.strlen($password).'.php'; + if($enforceNonCommonPassword && file_exists($passwordFile)) { + $commonPasswords = require_once $passwordFile; + if (isset($commonPasswords[strtolower($password)])) { + $message = 'Password is among the 1,000,000 most common ones. Please make it unique.'; + $message_t = $this->l->t( + 'Password is among the 1,000,000 most common ones. Please make it unique.' + ); + throw new HintException($message, $message_t); + } + } + } + +} diff --git a/lib/Validator/HIBPValidator.php b/lib/Validator/HIBPValidator.php new file mode 100644 index 00000000..9b13a37e --- /dev/null +++ b/lib/Validator/HIBPValidator.php @@ -0,0 +1,86 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCP\Http\Client\IClientService; +use OCP\IL10N; +use OCP\ILogger; + +class HIBPValidator implements IValidator { + + /** @var PasswordPolicyConfig */ + private $config; + /** @var IL10N */ + private $l; + /** @var IClientService */ + private $clientService; + /** @var ILogger */ + private $logger; + + public function __construct(PasswordPolicyConfig $config, + IL10N $l, + IClientService $clientService, + ILogger $logger) { + $this->config = $config; + $this->l = $l; + $this->clientService = $clientService; + $this->logger = $logger; + } + + public function validate(string $password): void { + if ($this->config->getEnforceHaveIBeenPwned()) { + $hash = sha1($password); + $range = substr($hash, 0, 5); + $needle = strtoupper(substr($hash, 5)); + + $client = $this->clientService->newClient(); + + try { + $response = $client->get( + 'https://api.pwnedpasswords.com/range/' . $range, + [ + 'timeout' => 5 + ] + ); + } catch (\Exception $e) { + $this->logger->logException($e, ['level' => ILogger::INFO]); + return true; + } + + $result = $response->getBody(); + + if (strpos($result, $needle) !== false) { + $message = 'Password is present in compromised password list. Please choose a different password.'; + $message_t = $this->l->t( + 'Password is present in compromised password list. Please choose a different password.' + ); + throw new HintException($message, $message_t); + } + } + } + +} diff --git a/lib/Validator/IValidator.php b/lib/Validator/IValidator.php new file mode 100644 index 00000000..73f3cd2e --- /dev/null +++ b/lib/Validator/IValidator.php @@ -0,0 +1,38 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; + +interface IValidator { + + /** + * @param string $password + * @return bool + * + * @throws HintException + */ + public function validate(string $password): void; +} diff --git a/lib/Validator/LengthValidator.php b/lib/Validator/LengthValidator.php new file mode 100644 index 00000000..400d444b --- /dev/null +++ b/lib/Validator/LengthValidator.php @@ -0,0 +1,54 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCP\IL10N; + +class LengthValidator implements IValidator { + + /** @var PasswordPolicyConfig */ + private $config; + /** @var IL10N */ + private $l; + + public function __construct(PasswordPolicyConfig $config, IL10N $l) { + $this->config = $config; + $this->l = $l; + } + + public function validate(string $password): void { + $minLength = $this->config->getMinLength(); + if(strlen($password) < $minLength) { + $message = 'Password needs to be at least ' . $minLength . ' characters long'; + $message_t = $this->l->t( + 'Password needs to be at least %s characters long', [$minLength] + ); + throw new HintException($message, $message_t); + } + } + +} diff --git a/lib/Validator/NumericCharacterValidator.php b/lib/Validator/NumericCharacterValidator.php new file mode 100644 index 00000000..1d0955e5 --- /dev/null +++ b/lib/Validator/NumericCharacterValidator.php @@ -0,0 +1,56 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCP\IL10N; + +class NumericCharacterValidator implements IValidator { + + /** @var PasswordPolicyConfig */ + private $config; + /** @var IL10N */ + private $l; + + public function __construct(PasswordPolicyConfig $config, IL10N $l) { + $this->config = $config; + $this->l = $l; + } + + public function validate(string $password): void { + $enforceNumericCharacters = $this->config->getEnforceNumericCharacters(); + if($enforceNumericCharacters) { + if (preg_match('/^(?=.*\d).+$/', $password) !== 1) { + $message = 'Password needs to contain at least one numeric character'; + $message_t = $this->l->t( + 'Password needs to contain at least one numeric character.' + ); + throw new HintException($message, $message_t); + } + } + } + +} diff --git a/lib/Validator/SpecialCharactersValidator.php b/lib/Validator/SpecialCharactersValidator.php new file mode 100644 index 00000000..e6d9478b --- /dev/null +++ b/lib/Validator/SpecialCharactersValidator.php @@ -0,0 +1,54 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCP\IL10N; + +class SpecialCharactersValidator implements IValidator { + + /** @var PasswordPolicyConfig */ + private $config; + /** @var IL10N */ + private $l; + + public function __construct(PasswordPolicyConfig $config, IL10N $l) { + $this->config = $config; + $this->l = $l; + } + + public function validate(string $password): void { + $enforceSpecialCharacters = $this->config->getEnforceSpecialCharacters(); + if($enforceSpecialCharacters && ctype_alnum($password)) { + $message = 'Password needs to contain at least one special character.'; + $message_t = $this->l->t( + 'Password needs to contain at least one special character.' + ); + throw new HintException($message, $message_t); + } + } + +} diff --git a/lib/Validator/UpperCaseLoweCaseValidator.php b/lib/Validator/UpperCaseLoweCaseValidator.php new file mode 100644 index 00000000..840007c7 --- /dev/null +++ b/lib/Validator/UpperCaseLoweCaseValidator.php @@ -0,0 +1,56 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCP\IL10N; + +class UpperCaseLoweCaseValidator implements IValidator { + + /** @var PasswordPolicyConfig */ + private $config; + /** @var IL10N */ + private $l; + + public function __construct(PasswordPolicyConfig $config, IL10N $l) { + $this->config = $config; + $this->l = $l; + } + + public function validate(string $password): void { + $enforceUpperLowerCase = $this->config->getEnforceUpperLowerCase(); + if($enforceUpperLowerCase) { + if (preg_match('/^(?=.*[a-z])(?=.*[A-Z]).+$/', $password) !== 1) { + $message = 'Password needs to contain at least one lower and one upper case character.'; + $message_t = $this->l->t( + 'Password needs to contain at least one lower and one upper case character.' + ); + throw new HintException($message, $message_t); + } + } + } + +} diff --git a/tests/lib/controller/PasswordPolicyConfigTest.php b/tests/lib/PasswordPolicyConfigTest.php similarity index 98% rename from tests/lib/controller/PasswordPolicyConfigTest.php rename to tests/lib/PasswordPolicyConfigTest.php index f6165a0c..5d6c1d9c 100644 --- a/tests/lib/controller/PasswordPolicyConfigTest.php +++ b/tests/lib/PasswordPolicyConfigTest.php @@ -19,15 +19,13 @@ * */ - namespace OCA\Password_Policy\Tests; - use OCA\Password_Policy\PasswordPolicyConfig; use OCP\IConfig; use Test\TestCase; -class ConfigTest extends TestCase { +class PasswordPolicyConfigTest extends TestCase { /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ private $config; diff --git a/tests/lib/PasswordValidatorTest.php b/tests/lib/PasswordValidatorTest.php new file mode 100644 index 00000000..457212c2 --- /dev/null +++ b/tests/lib/PasswordValidatorTest.php @@ -0,0 +1,96 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Tests; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCA\Password_Policy\PasswordValidator; +use OCA\Password_Policy\Validator\CommonPasswordsValidator; +use OCA\Password_Policy\Validator\HIBPValidator; +use OCA\Password_Policy\Validator\IValidator; +use OCA\Password_Policy\Validator\LengthValidator; +use OCA\Password_Policy\Validator\NumericCharacterValidator; +use OCA\Password_Policy\Validator\SpecialCharactersValidator; +use OCA\Password_Policy\Validator\UpperCaseLoweCaseValidator; +use OCP\AppFramework\IAppContainer; +use OCP\AppFramework\QueryException; +use OCP\Http\Client\IClientService; +use OCP\IL10N; +use OCP\ILogger; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class PasswordValidatorTest extends TestCase { + + /** @var IAppContainer|MockObject */ + private $container; + + /** @var ILogger|MockObject */ + private $logger; + + /** @var PasswordValidator */ + private $validator; + + + public function setUp() { + parent::setUp(); + + $this->container = $this->createMock(IAppContainer::class); + $this->logger = $this->createMock(ILogger::class); + + $this->validator = new PasswordValidator($this->container, $this->logger); + } + + public function testValidate() { + $validators = [ + CommonPasswordsValidator::class, + LengthValidator::class, + NumericCharacterValidator::class, + UpperCaseLoweCaseValidator::class, + SpecialCharactersValidator::class, + HIBPValidator::class, + ]; + + $this->container->method('query') + ->willReturnCallback(function($class) use (&$validators) { + if (($key = array_search($class, $validators)) !== false) { + $validator = $this->createMock(IValidator::class); + $validator->expects($this->once()) + ->method('validate') + ->with('password'); + + unset($validators[$key]); + + return $validator; + } + + throw new QueryException(); + }); + + $this->logger->expects($this->never())->method($this->anything()); + + $this->validator->validate('password'); + $this->assertEmpty($validators); + } + + +} diff --git a/tests/lib/Validator/CommonPasswordsValidatorTest.php b/tests/lib/Validator/CommonPasswordsValidatorTest.php new file mode 100644 index 00000000..68b15143 --- /dev/null +++ b/tests/lib/Validator/CommonPasswordsValidatorTest.php @@ -0,0 +1,81 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Tests\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCA\Password_Policy\Validator\CommonPasswordsValidator; +use OCA\Password_Policy\Validator\IValidator; +use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class CommonPasswordsValidatorTest extends TestCase { + + /** @var PasswordPolicyConfig|MockObject */ + private $confg; + + /** @var IL10N|MockObject */ + private $l; + + /** @var IValidator */ + private $validator; + + public function setUp() { + parent::setUp(); + + $this->confg = $this->createMock(PasswordPolicyConfig::class); + $this->l = $this->createMock(IL10N::class); + + $this->validator = new CommonPasswordsValidator( + $this->confg, + $this->l + ); + } + + /** + * @dataProvider dataValidate + */ + public function testValidate(string $password, bool $enforced, bool $valid) { + $this->confg->method('getEnforceNonCommonPassword') + ->willReturn($enforced); + + if (!$valid) { + $this->expectException(HintException::class); + } + + $this->assertTrue(true); + $this->validator->validate($password); + } + + public function dataValidate() { + return [ + ['banana', false, true], + ['bananabananabananabanana', false, true], + ['banana', true, false], + ['bananabananabananabanana', true, true], + ]; + } +} diff --git a/tests/lib/Validator/LengthValidatorTest.php b/tests/lib/Validator/LengthValidatorTest.php new file mode 100644 index 00000000..02d08c63 --- /dev/null +++ b/tests/lib/Validator/LengthValidatorTest.php @@ -0,0 +1,80 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Tests\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCA\Password_Policy\Validator\IValidator; +use OCA\Password_Policy\Validator\LengthValidator; +use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class LengthValidatorTest extends TestCase { + + /** @var PasswordPolicyConfig|MockObject */ + private $confg; + + /** @var IL10N|MockObject */ + private $l; + + /** @var IValidator */ + private $validator; + + public function setUp() { + parent::setUp(); + + $this->confg = $this->createMock(PasswordPolicyConfig::class); + $this->l = $this->createMock(IL10N::class); + + $this->validator = new LengthValidator( + $this->confg, + $this->l + ); + } + + /** + * @dataProvider dataValidate + */ + public function testValidate(string $password, int $length, bool $valid) { + $this->confg->method('getMinLength') + ->willReturn($length); + + if (!$valid) { + $this->expectException(HintException::class); + } + + $this->assertTrue(true); + $this->validator->validate($password); + } + + public function dataValidate() { + return [ + ['password', 10, false], + ['password', 8, true], + ['password', 6, true], + ]; + } +} diff --git a/tests/lib/Validator/NumericCharacterValidatorTest.php b/tests/lib/Validator/NumericCharacterValidatorTest.php new file mode 100644 index 00000000..94634d5d --- /dev/null +++ b/tests/lib/Validator/NumericCharacterValidatorTest.php @@ -0,0 +1,81 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Tests\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCA\Password_Policy\Validator\IValidator; +use OCA\Password_Policy\Validator\NumericCharacterValidator; +use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class NumericCharacterValidatorTest extends TestCase { + + /** @var PasswordPolicyConfig|MockObject */ + private $confg; + + /** @var IL10N|MockObject */ + private $l; + + /** @var IValidator */ + private $validator; + + public function setUp() { + parent::setUp(); + + $this->confg = $this->createMock(PasswordPolicyConfig::class); + $this->l = $this->createMock(IL10N::class); + + $this->validator = new NumericCharacterValidator( + $this->confg, + $this->l + ); + } + + /** + * @dataProvider dataValidate + */ + public function testValidate(string $password, bool $enforced, bool $valid) { + $this->confg->method('getEnforceNumericCharacters') + ->willReturn($enforced); + + if (!$valid) { + $this->expectException(HintException::class); + } + + $this->assertTrue(true); + $this->validator->validate($password); + } + + public function dataValidate() { + return [ + ['password', false, true], + ['password1', false, true], + ['password', true, false], + ['password1', true, true], + ]; + } +} diff --git a/tests/lib/Validator/SpecialCharactersValidatorTest.php b/tests/lib/Validator/SpecialCharactersValidatorTest.php new file mode 100644 index 00000000..27e8d17c --- /dev/null +++ b/tests/lib/Validator/SpecialCharactersValidatorTest.php @@ -0,0 +1,81 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Tests\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCA\Password_Policy\Validator\IValidator; +use OCA\Password_Policy\Validator\SpecialCharactersValidator; +use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class SpecialCharactersValidatorTest extends TestCase { + + /** @var PasswordPolicyConfig|MockObject */ + private $confg; + + /** @var IL10N|MockObject */ + private $l; + + /** @var IValidator */ + private $validator; + + public function setUp() { + parent::setUp(); + + $this->confg = $this->createMock(PasswordPolicyConfig::class); + $this->l = $this->createMock(IL10N::class); + + $this->validator = new SpecialCharactersValidator( + $this->confg, + $this->l + ); + } + + /** + * @dataProvider dataValidate + */ + public function testValidate(string $password, bool $enforced, bool $valid) { + $this->confg->method('getEnforceSpecialCharacters') + ->willReturn($enforced); + + if (!$valid) { + $this->expectException(HintException::class); + } + + $this->assertTrue(true); + $this->validator->validate($password); + } + + public function dataValidate() { + return [ + ['password', false, true], + ['p@ssword', false, true], + ['password', true, false], + ['p@ssword', true, true], + ]; + } +} diff --git a/tests/lib/Validator/UpperCaseLowerCaseValidatorTest.php b/tests/lib/Validator/UpperCaseLowerCaseValidatorTest.php new file mode 100644 index 00000000..0999c923 --- /dev/null +++ b/tests/lib/Validator/UpperCaseLowerCaseValidatorTest.php @@ -0,0 +1,81 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Password_Policy\Tests\Validator; + +use OC\HintException; +use OCA\Password_Policy\PasswordPolicyConfig; +use OCA\Password_Policy\Validator\IValidator; +use OCA\Password_Policy\Validator\UpperCaseLoweCaseValidator; +use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class UpperCaseLowerCaseValidatorTest extends TestCase { + + /** @var PasswordPolicyConfig|MockObject */ + private $confg; + + /** @var IL10N|MockObject */ + private $l; + + /** @var IValidator */ + private $validator; + + public function setUp() { + parent::setUp(); + + $this->confg = $this->createMock(PasswordPolicyConfig::class); + $this->l = $this->createMock(IL10N::class); + + $this->validator = new UpperCaseLoweCaseValidator( + $this->confg, + $this->l + ); + } + + /** + * @dataProvider dataValidate + */ + public function testValidate(string $password, bool $enforced, bool $valid) { + $this->confg->method('getEnforceUpperLowerCase') + ->willReturn($enforced); + + if (!$valid) { + $this->expectException(HintException::class); + } + + $this->assertTrue(true); + $this->validator->validate($password); + } + + public function dataValidate() { + return [ + ['password', false, true], + ['passWord', false, true], + ['password', true, false], + ['passWord', true, true], + ]; + } +} diff --git a/tests/lib/controller/PasswordValidatorTest.php b/tests/lib/controller/PasswordValidatorTest.php deleted file mode 100644 index 1a1b8e52..00000000 --- a/tests/lib/controller/PasswordValidatorTest.php +++ /dev/null @@ -1,242 +0,0 @@ - - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -namespace OCA\Password_Policy\Tests; - - -use OC\HintException; -use OCA\Password_Policy\PasswordPolicyConfig; -use OCA\Password_Policy\PasswordValidator; -use OCP\Http\Client\IClientService; -use OCP\IL10N; -use Test\TestCase; - -class PasswordValidatorTest extends TestCase { - - /** @var PasswordPolicyConfig|\PHPUnit_Framework_MockObject_MockObject */ - private $config; - - /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ - private $l10n; - - /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */ - private $clientService; - - public function setUp() { - parent::setUp(); - - $this->l10n = $this->createMock(IL10N::class); - $this->config = $this->createMock(PasswordPolicyConfig::class); - $this->clientService = $this->createMock(IClientService::class); - } - - /** - * @param array $mockedMethods - * @return PasswordValidator | \PHPUnit_Framework_MockObject_MockObject - */ - private function getInstance($mockedMethods = []) { - $passwordValidator = $this->getMockBuilder(PasswordValidator::class) - ->setConstructorArgs([$this->config, $this->l10n, $this->clientService]) - ->setMethods($mockedMethods)->getMock(); - - return $passwordValidator; - } - - public function testCheckPasswordLength() { - $this->config->expects($this->exactly(2))->method('getMinLength')->willReturn(4); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkPasswordLength', ['password']); - $this->invokePrivate($instance, 'checkPasswordLength', ['1234']); - } - - /** - * @expectedException \OC\HintException - */ - public function testCheckPasswordLengthFail() { - $this->config->expects($this->once())->method('getMinLength')->willReturn(4); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkPasswordLength', ['123']); - } - - /** - * @dataProvider dataTestCheckUpperLowerCase - * - * @param string $password - * @param bool $enforceUpperLowerCase - */ - public function testCheckUpperLowerCase($password, $enforceUpperLowerCase) { - $this->config->expects($this->once())->method('getEnforceUpperLowerCase') - ->willReturn($enforceUpperLowerCase); - - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkUpperLowerCase', [$password]); - } - - public function dataTestCheckUpperLowerCase() { - return [ - ['passWord', true], - ['PASSwORD', true], - ['password', false], - ['PASSWORD', false], - ]; - } - - - /** - * @dataProvider dataTestCheckUpperLowerCaseFail - * @expectedException \OC\HintException - */ - public function testCheckUpperLowerCaseFail($password) { - $this->config->expects($this->once())->method('getEnforceUpperLowerCase')->willReturn(true); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkUpperLowerCase', [$password]); - } - - public function dataTestCheckUpperLowerCaseFail() { - return [ - ['password'], ['PASSWORD'] - ]; - } - - - /** - * @dataProvider dataTestCheckNumericCharacters - * - * @param string $password - * @param bool $enforceNumericCharacters - */ - public function testCheckNumericCharacters($password, $enforceNumericCharacters) { - $this->config->expects($this->once())->method('getEnforceNumericCharacters') - ->willReturn($enforceNumericCharacters); - - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkNumericCharacters', [$password]); - } - - public function dataTestCheckNumericCharacters() { - return [ - ['password42', true], - ['password', false] - ]; - } - - - /** - * @expectedException \OC\HintException - */ - public function testCheckNumericCharactersFail() { - - $password = 'pass%word'; - - $this->config->expects($this->once())->method('getEnforceNumericCharacters')->willReturn(true); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkNumericCharacters', [$password]); - } - - /** - * @dataProvider dataTestCheckSpecialCharacters - * - * @param string $password - * @param bool $enforceSpecialCharacters - */ - public function testCheckSpecialCharacters($password, $enforceSpecialCharacters) { - $this->config->expects($this->once())->method('getEnforceSpecialCharacters') - ->willReturn($enforceSpecialCharacters); - - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkSpecialCharacters', [$password]); - } - - public function dataTestCheckSpecialCharacters() { - return [ - ['pass%word', true], - ['password', false] - ]; - } - - - /** - * @expectedException \OC\HintException - */ - public function testCheckSpecialCharactersFail() { - - $password = 'password42'; - - $this->config->expects($this->once())->method('getEnforceSpecialCharacters')->willReturn(true); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkSpecialCharacters', [$password]); - } - - /** - * @expectedException \OC\HintException - */ - public function testCheckCommonPasswordsFail() { - $password = 'banana'; - - $this->config->expects($this->once())->method('getEnforceNonCommonPassword')->willReturn(true); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkCommonPasswords', [$password]); - } - - public function testCheckCommonPasswordsPass() { - $password = 'banana1038462'; - - $this->config->expects($this->once())->method('getEnforceNonCommonPassword')->willReturn(true); - $instance = $this->getInstance(); - - $this->invokePrivate($instance, 'checkCommonPasswords', [$password]); - } - - public function testValidate() { - - $password = 'password'; - - $instance = $this->getInstance( - [ - 'checkPasswordLength', - 'checkUpperLowerCase', - 'checkNumericCharacters', - 'checkSpecialCharacters', - 'checkCommonPasswords', - ] - ); - - $instance->expects($this->once())->method('checkPasswordLength')->with($password); - $instance->expects($this->once())->method('checkUpperLowerCase')->with($password); - $instance->expects($this->once())->method('checkNumericCharacters')->with($password); - $instance->expects($this->once())->method('checkSpecialCharacters')->with($password); - $instance->expects($this->once())->method('checkCommonPasswords')->with($password); - - $instance->validate($password); - } - - -}