From 127cacdd19e43dd56d5c8dc5ae174228bdfe0021 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 21 Aug 2024 20:23:27 +0200 Subject: [PATCH] feat(Security): Allow setting password context for validation and generation Co-authored-by: Ferdinand Thiessen Co-authored-by: Joas Schilling <213943+nickvergessen@users.noreply.github.com> Signed-off-by: Ferdinand Thiessen --- lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + .../Events/GenerateSecurePasswordEvent.php | 34 +++++++++++++++++-- .../Events/ValidatePasswordPolicyEvent.php | 23 ++++++++++--- lib/public/Security/PasswordContext.php | 29 ++++++++++++++++ .../GenerateSecurePasswordEventTest.php | 33 ++++++++++++++++++ .../ValidatePasswordPolicyEventTest.php | 28 +++++++++++++++ 7 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 lib/public/Security/PasswordContext.php create mode 100644 tests/lib/Security/Events/GenerateSecurePasswordEventTest.php create mode 100644 tests/lib/Security/Events/ValidatePasswordPolicyEventTest.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 1a3a86fd207..eed05ce73d6 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -678,6 +678,7 @@ return array( 'OCP\\Security\\Ip\\IFactory' => $baseDir . '/lib/public/Security/Ip/IFactory.php', 'OCP\\Security\\Ip\\IRange' => $baseDir . '/lib/public/Security/Ip/IRange.php', 'OCP\\Security\\Ip\\IRemoteAddress' => $baseDir . '/lib/public/Security/Ip/IRemoteAddress.php', + 'OCP\\Security\\PasswordContext' => $baseDir . '/lib/public/Security/PasswordContext.php', 'OCP\\Security\\RateLimiting\\ILimiter' => $baseDir . '/lib/public/Security/RateLimiting/ILimiter.php', 'OCP\\Security\\RateLimiting\\IRateLimitExceededException' => $baseDir . '/lib/public/Security/RateLimiting/IRateLimitExceededException.php', 'OCP\\Security\\VerificationToken\\IVerificationToken' => $baseDir . '/lib/public/Security/VerificationToken/IVerificationToken.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index c636ba769f9..ac293dd7b36 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -711,6 +711,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Security\\Ip\\IFactory' => __DIR__ . '/../../..' . '/lib/public/Security/Ip/IFactory.php', 'OCP\\Security\\Ip\\IRange' => __DIR__ . '/../../..' . '/lib/public/Security/Ip/IRange.php', 'OCP\\Security\\Ip\\IRemoteAddress' => __DIR__ . '/../../..' . '/lib/public/Security/Ip/IRemoteAddress.php', + 'OCP\\Security\\PasswordContext' => __DIR__ . '/../../..' . '/lib/public/Security/PasswordContext.php', 'OCP\\Security\\RateLimiting\\ILimiter' => __DIR__ . '/../../..' . '/lib/public/Security/RateLimiting/ILimiter.php', 'OCP\\Security\\RateLimiting\\IRateLimitExceededException' => __DIR__ . '/../../..' . '/lib/public/Security/RateLimiting/IRateLimitExceededException.php', 'OCP\\Security\\VerificationToken\\IVerificationToken' => __DIR__ . '/../../..' . '/lib/public/Security/VerificationToken/IVerificationToken.php', diff --git a/lib/public/Security/Events/GenerateSecurePasswordEvent.php b/lib/public/Security/Events/GenerateSecurePasswordEvent.php index 8adddd529b0..419e7b40ee4 100644 --- a/lib/public/Security/Events/GenerateSecurePasswordEvent.php +++ b/lib/public/Security/Events/GenerateSecurePasswordEvent.php @@ -9,15 +9,34 @@ declare(strict_types=1); namespace OCP\Security\Events; use OCP\EventDispatcher\Event; +use OCP\Security\PasswordContext; /** + * Event to request a secure password to be generated * @since 18.0.0 */ class GenerateSecurePasswordEvent extends Event { - /** @var null|string */ - private $password; + private ?string $password; /** + * Request a secure password to be generated. + * + * By default passwords are generated for the user account context, + * this can be adjusted by passing another `PasswordContext`. + * @since 31.0.0 + */ + public function __construct( + private PasswordContext $context = PasswordContext::ACCOUNT, + ) { + parent::__construct(); + $this->password = null; + } + + /** + * Get the generated password. + * + * If a password generator is registered and successfully generated a password + * that password can get read back. Otherwise `null` is returned. * @since 18.0.0 */ public function getPassword(): ?string { @@ -25,9 +44,20 @@ class GenerateSecurePasswordEvent extends Event { } /** + * Set the generated password. + * + * This is used by password generators to set the generated password. * @since 18.0.0 */ public function setPassword(string $password): void { $this->password = $password; } + + /** + * Get the context this password should generated for. + * @since 31.0.0 + */ + public function getContext(): PasswordContext { + return $this->context; + } } diff --git a/lib/public/Security/Events/ValidatePasswordPolicyEvent.php b/lib/public/Security/Events/ValidatePasswordPolicyEvent.php index 0aa8b516f70..d7ac9442392 100644 --- a/lib/public/Security/Events/ValidatePasswordPolicyEvent.php +++ b/lib/public/Security/Events/ValidatePasswordPolicyEvent.php @@ -9,26 +9,41 @@ declare(strict_types=1); namespace OCP\Security\Events; use OCP\EventDispatcher\Event; +use OCP\Security\PasswordContext; /** + * This event can be emitted to request a validation of a password. + * + * If a password policy app is installed and the password + * is invalid, an `\OCP\HintException` will be thrown. * @since 18.0.0 */ class ValidatePasswordPolicyEvent extends Event { - /** @var string */ - private $password; /** * @since 18.0.0 + * @since 31.0.0 - $context parameter added */ - public function __construct(string $password) { + public function __construct( + private string $password, + private PasswordContext $context = PasswordContext::ACCOUNT, + ) { parent::__construct(); - $this->password = $password; } /** + * Get the password that should be validated. * @since 18.0.0 */ public function getPassword(): string { return $this->password; } + + /** + * Get the context this password should validated for. + * @since 31.0.0 + */ + public function getContext(): PasswordContext { + return $this->context; + } } diff --git a/lib/public/Security/PasswordContext.php b/lib/public/Security/PasswordContext.php new file mode 100644 index 00000000000..909070c09ff --- /dev/null +++ b/lib/public/Security/PasswordContext.php @@ -0,0 +1,29 @@ +assertNull($event->getPassword()); + $this->assertEquals(PasswordContext::ACCOUNT, $event->getContext()); + } + + public function testSettingPassword() { + $event = new GenerateSecurePasswordEvent(); + $event->setPassword('example'); + $this->assertEquals('example', $event->getPassword()); + } + + public function testSettingContext() { + $event = new GenerateSecurePasswordEvent(PasswordContext::SHARING); + $this->assertEquals(PasswordContext::SHARING, $event->getContext()); + } +} diff --git a/tests/lib/Security/Events/ValidatePasswordPolicyEventTest.php b/tests/lib/Security/Events/ValidatePasswordPolicyEventTest.php new file mode 100644 index 00000000000..8a7bc3b2f29 --- /dev/null +++ b/tests/lib/Security/Events/ValidatePasswordPolicyEventTest.php @@ -0,0 +1,28 @@ +assertEquals($password, $event->getPassword()); + $this->assertEquals(PasswordContext::ACCOUNT, $event->getContext()); + } + + public function testSettingContext() { + $event = new ValidatePasswordPolicyEvent('example', PasswordContext::SHARING); + $this->assertEquals(PasswordContext::SHARING, $event->getContext()); + } +} -- 2.39.5