Skip to content
This repository has been archived by the owner on Jul 24, 2023. It is now read-only.

Commit

Permalink
Add ability to set custom password strategies + add makeSSHAPassword …
Browse files Browse the repository at this point in the history
…method to Utilities that can be used as one of the strategies
  • Loading branch information
ekvedaras committed Nov 23, 2019
1 parent c229325 commit 84409fc
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
/.idea
/vendor
composer.lock
33 changes: 29 additions & 4 deletions src/Models/User.php
Expand Up @@ -23,6 +23,31 @@ class User extends Entry implements Authenticatable
Concerns\HasLastLogonAndLogOff,
Concerns\HasUserAccountControl;

/** @var callable|null */
private static $passwordStrategy;

/**
* Password will be processed using given callback before saving.
*
* @param callable $strategy
*/
public static function usePasswordStrategy(callable $strategy)
{
self::$passwordStrategy = $strategy;
}

/**
* Will return user set password strategy or default one.
*
* @return callable
*/
public static function getPasswordStrategy(): callable
{
return self::$passwordStrategy ?? function ($password) {
return Utilities::encodePassword($password);
};
}

/**
* Get the name of the unique identifier for the user.
*
Expand Down Expand Up @@ -788,7 +813,7 @@ public function setPassword($password)
{
$this->validateSecureConnection();

$encodedPassword = Utilities::encodePassword($password);
$encodedPassword = call_user_func(self::getPasswordStrategy(), $password);

if ($this->exists) {
// If the record exists, we need to add a batch replace
Expand Down Expand Up @@ -861,21 +886,21 @@ public function changePassword($oldPassword, $newPassword, $replaceNotRemove = f
$modifications[] = $this->newBatchModification(
$attribute,
LDAP_MODIFY_BATCH_REPLACE,
[Utilities::encodePassword($newPassword)]
[call_user_func(self::getPasswordStrategy(), $newPassword)]
);
} else {
// Create batch modification for removing the old password.
$modifications[] = $this->newBatchModification(
$attribute,
LDAP_MODIFY_BATCH_REMOVE,
[Utilities::encodePassword($oldPassword)]
[call_user_func(self::getPasswordStrategy(), $oldPassword)]
);

// Create batch modification for adding the new password.
$modifications[] = $this->newBatchModification(
$attribute,
LDAP_MODIFY_BATCH_ADD,
[Utilities::encodePassword($newPassword)]
[call_user_func(self::getPasswordStrategy(), $newPassword)]
);
}

Expand Down
15 changes: 15 additions & 0 deletions src/Utilities.php
Expand Up @@ -152,6 +152,21 @@ public static function encodePassword($password)
return iconv('UTF-8', 'UTF-16LE', '"'.$password.'"');
}

/**
* Salt and hash a password to make its SSHA OpenLDAP version.
*
* @param string $password The password to create
*
* @return string
*/
public static function makeSSHAPassword($password)
{
mt_srand((float) microtime() * 1000000);
$salt = pack('CCCC', mt_rand(), mt_rand(), mt_rand(), mt_rand());

return '{SSHA}'.base64_encode(pack('H*', sha1($password.$salt)).$salt);
}

/**
* Round a Windows timestamp down to seconds and remove
* the seconds between 1601-01-01 and 1970-01-01.
Expand Down
27 changes: 27 additions & 0 deletions tests/Models/UserTest.php
Expand Up @@ -44,6 +44,33 @@ public function test_set_password_on_new_user()
$this->assertEquals($expected, $user->getModifications());
}

public function test_set_password_on_new_user_with_different_password_strategy()
{
$connection = $this->newConnectionMock();

$connection->shouldReceive('canChangePasswords')->once()->andReturn(true);

$user = new User([], $this->newBuilder($connection));
$rand = mt_rand();
User::usePasswordStrategy(function ($password) use ($rand) {
return $rand.$password;
});

$password = 'password';

$user->setPassword($password);

$expected = [
[
'attrib' => 'unicodepwd',
'modtype' => 1,
'values' => [$rand.$password],
],
];

$this->assertEquals($expected, $user->getModifications());
}

public function test_set_password_on_existing_user()
{
$connection = $this->newConnectionMock();
Expand Down
9 changes: 9 additions & 0 deletions tests/TestCase.php
Expand Up @@ -3,6 +3,8 @@
namespace Adldap\Tests;

use Mockery;
use Adldap\Utilities;
use Adldap\Models\User;
use Adldap\Query\Builder;
use Adldap\Query\Grammar;
use Adldap\Connections\ConnectionInterface;
Expand Down Expand Up @@ -33,6 +35,13 @@ public function setUp()
}
}

protected function tearDown()
{
User::usePasswordStrategy(function ($password) {
return Utilities::encodePassword($password);
});
}

/**
* Mocks a the specified class.
*
Expand Down
10 changes: 10 additions & 0 deletions tests/UtilitiesTest.php
Expand Up @@ -43,6 +43,16 @@ public function test_encode_password()
$this->assertEquals($expected, bin2hex($encoded));
}

public function test_make_ssha_password()
{
$password = 'password';

$encoded = Utilities::makeSSHAPassword($password);

$this->assertStringStartsWith('{SSHA}', $encoded);
$this->assertGreaterThan(6, strlen($encoded));
}

public function test_is_valid_sid()
{
$this->assertTrue(Utilities::isValidSid('S-1-5-21-3623811015-3361044348-30300820-1013'));
Expand Down

0 comments on commit 84409fc

Please sign in to comment.