diff --git a/src/Models/Attributes/Password.php b/src/Models/Attributes/Password.php index 71f4d06e..f38ae95b 100644 --- a/src/Models/Attributes/Password.php +++ b/src/Models/Attributes/Password.php @@ -102,6 +102,22 @@ public static function md5(string $password): string return '{MD5}'.static::makeHash($password, 'md5'); } + /** + * Make an argon2i password. + */ + public static function argon2i(string $password): string + { + return '{ARGON2I}'.password_hash($password, PASSWORD_ARGON2I); + } + + /** + * Make an argon2id password. + */ + public static function argon2id(string $password): string + { + return '{ARGON2ID}'.password_hash($password, PASSWORD_ARGON2ID); + } + /** * Make a non-salted NThash password. */ diff --git a/src/Models/Concerns/HasPassword.php b/src/Models/Concerns/HasPassword.php index 8eb01ef9..6b55ce2d 100644 --- a/src/Models/Concerns/HasPassword.php +++ b/src/Models/Concerns/HasPassword.php @@ -29,6 +29,12 @@ public function setPasswordAttribute(array|string $password): void // If the password given is an array, we can assume we // are changing the password for the current user. if (is_array($password)) { + if (in_array(strtolower($method), ['argon2i', 'argon2id'])) { + throw new LdapRecordException( + "Argon2 passwords cannot be changed using this method. Use the LDAP Password Modify extended operation instead." + ); + } + $this->setChangedPassword( $this->getHashedPassword($method, $password[0], $this->getPasswordSalt($method)), $this->getHashedPassword($method, $password[1]), diff --git a/tests/Unit/Models/Attributes/PasswordTest.php b/tests/Unit/Models/Attributes/PasswordTest.php index 67a2c45c..7340b90c 100644 --- a/tests/Unit/Models/Attributes/PasswordTest.php +++ b/tests/Unit/Models/Attributes/PasswordTest.php @@ -92,6 +92,22 @@ public function test_sha512crypt() $this->assertEquals($password, Password::sha512crypt('password', Password::getSalt($password))); } + public function test_argon2i() + { + $password = Password::argon2i('password'); + + $this->assertStringStartsWith('{ARGON2I}$argon2i$', $password); + $this->assertNotEquals($password, Password::argon2i('password')); + } + + public function test_argon2id() + { + $password = Password::argon2id('password'); + + $this->assertStringStartsWith('{ARGON2ID}$argon2id$', $password); + $this->assertNotEquals($password, Password::argon2id('password')); + } + // Unsalted Hash Tests. // public function test_sha() diff --git a/tests/Unit/Models/OpenLDAP/UserTest.php b/tests/Unit/Models/OpenLDAP/UserTest.php index e03a4d11..3e0b5541 100644 --- a/tests/Unit/Models/OpenLDAP/UserTest.php +++ b/tests/Unit/Models/OpenLDAP/UserTest.php @@ -4,6 +4,7 @@ use LdapRecord\Connection; use LdapRecord\Container; +use LdapRecord\LdapRecordException; use LdapRecord\Models\Attributes\Password; use LdapRecord\Models\OpenLDAP\User; use LdapRecord\Tests\TestCase; @@ -75,6 +76,20 @@ public function test_algo_and_salt_is_automatically_detected_when_changing_a_use $this->assertEquals(Password::CRYPT_SALT_TYPE_SHA512, $newAlgo); } + public function test_changing_argon2_password_throws_exception() + { + $this->expectException(LdapRecordException::class); + $this->expectExceptionMessage('Argon2 passwords cannot be changed using this method.'); + + $user = (new OpenLDAPUserTestStub)->setRawAttributes([ + 'userpassword' => [ + Password::argon2id('secret'), + ], + ]); + + $user->password = ['secret', 'new-secret']; + } + public function test_correct_auth_identifier_is_returned() { $entryUuid = 'foo';