blob: 98224566435d6b93137315200ae9121fb449f908 [file] [log] [blame]
<?php
namespace LdapRecord\Models\Concerns;
use LdapRecord\ConnectionException;
use LdapRecord\LdapRecordException;
use LdapRecord\Models\Attributes\Password;
trait HasPassword
{
/**
* Set the password on the user.
*
* @param string|array $password
*
* @throws ConnectionException
*/
public function setPasswordAttribute($password)
{
$this->validateSecureConnection();
// Here we will attempt to determine the password hash method in use
// by parsing the users hashed password (if it as available). If a
// method is determined, we will override the default here.
if (! ($method = $this->determinePasswordHashMethod())) {
$method = $this->getPasswordHashMethod();
}
// If the password given is an array, we can assume we
// are changing the password for the current user.
if (is_array($password)) {
$this->setChangedPassword(
$this->getHashedPassword($method, $password[0], $this->getPasswordSalt($method)),
$this->getHashedPassword($method, $password[1]),
$this->getPasswordAttributeName()
);
}
// Otherwise, we will assume the password is being
// reset, overwriting the one currently in place.
else {
$this->setPassword(
$this->getHashedPassword($method, $password),
$this->getPasswordAttributeName()
);
}
}
/**
* Alias for setting the password on the user.
*
* @param string|array $password
*
* @throws ConnectionException
*/
public function setUnicodepwdAttribute($password)
{
$this->setPasswordAttribute($password);
}
/**
* An accessor for retrieving the user's hashed password value.
*
* @return string|null
*/
public function getPasswordAttribute()
{
return $this->getAttribute($this->getPasswordAttributeName())[0] ?? null;
}
/**
* Get the name of the attribute that contains the user's password.
*
* @return string
*/
public function getPasswordAttributeName()
{
if (property_exists($this, 'passwordAttribute')) {
return $this->passwordAttribute;
}
if (method_exists($this, 'passwordAttribute')) {
return $this->passwordAttribute();
}
return 'unicodepwd';
}
/**
* Get the name of the method to use for hashing the user's password.
*
* @return string
*/
public function getPasswordHashMethod()
{
if (property_exists($this, 'passwordHashMethod')) {
return $this->passwordHashMethod;
}
if (method_exists($this, 'passwordHashMethod')) {
return $this->passwordHashMethod();
}
return 'encode';
}
/**
* Set the changed password.
*
* @param string $oldPassword
* @param string $newPassword
* @param string $attribute
*
* @return void
*/
protected function setChangedPassword($oldPassword, $newPassword, $attribute)
{
// Create batch modification for removing the old password.
$this->addModification(
$this->newBatchModification(
$attribute,
LDAP_MODIFY_BATCH_REMOVE,
[$oldPassword]
)
);
// Create batch modification for adding the new password.
$this->addModification(
$this->newBatchModification(
$attribute,
LDAP_MODIFY_BATCH_ADD,
[$newPassword]
)
);
}
/**
* Set the password on the model.
*
* @param string $password
* @param string $attribute
*
* @return void
*/
protected function setPassword($password, $attribute)
{
$this->addModification(
$this->newBatchModification(
$attribute,
LDAP_MODIFY_BATCH_REPLACE,
[$password]
)
);
}
/**
* Encode / hash the given password.
*
* @param string $method
* @param string $password
* @param string $salt
*
* @throws LdapRecordException
*
* @return string
*/
protected function getHashedPassword($method, $password, $salt = null)
{
if (! method_exists(Password::class, $method)) {
throw new LdapRecordException("Password hashing method [{$method}] does not exist.");
}
if (Password::hashMethodRequiresSalt($method)) {
return Password::{$method}($password, $salt);
}
return Password::{$method}($password);
}
/**
* Validates that the current LDAP connection is secure.
*
* @throws ConnectionException
*
* @return void
*/
protected function validateSecureConnection()
{
$connection = $this->getConnection();
if ($connection->isConnected()) {
$secure = $connection->getLdapConnection()->canChangePasswords();
} else {
$secure = $connection->getConfiguration()->get('use_ssl') || $connection->getConfiguration()->get('use_tls');
}
if (! $secure) {
throw new ConnectionException(
'You must be connected to your LDAP server with TLS or SSL to perform this operation.'
);
}
}
/**
* Attempt to retrieve the password's salt.
*
* @param string $method
*
* @return string|null
*/
public function getPasswordSalt($method)
{
if (! Password::hashMethodRequiresSalt($method)) {
return;
}
return Password::getSalt($this->password);
}
/**
* Determine the password hash method to use from the users current password.
*
* @return string|void
*/
public function determinePasswordHashMethod()
{
if (! $password = $this->password) {
return;
}
if (! $method = Password::getHashMethod($password)) {
return;
}
[,$algo] = array_pad(
Password::getHashMethodAndAlgo($password) ?? [],
$length = 2,
$value = null
);
switch ($algo) {
case Password::CRYPT_SALT_TYPE_MD5:
return 'md5'.$method;
case Password::CRYPT_SALT_TYPE_SHA256:
return 'sha256'.$method;
case Password::CRYPT_SALT_TYPE_SHA512:
return 'sha512'.$method;
default:
return $method;
}
}
}