diff --git a/application/Espo/Core/Authentication/AuthToken/Data.php b/application/Espo/Core/Authentication/AuthToken/Data.php index 075ed04b3b..669c3ec788 100644 --- a/application/Espo/Core/Authentication/AuthToken/Data.php +++ b/application/Espo/Core/Authentication/AuthToken/Data.php @@ -30,6 +30,7 @@ namespace Espo\Core\Authentication\AuthToken; use RuntimeException; +use SensitiveParameter; /** * An auth token data. Used for auth token creation. @@ -96,7 +97,7 @@ class Data * createSecret?: ?bool, * } $data */ - public static function create(array $data): self + public static function create(#[SensitiveParameter] array $data): self { $obj = new self(); diff --git a/application/Espo/Core/Authentication/Helper/UserFinder.php b/application/Espo/Core/Authentication/Helper/UserFinder.php index 5030da70ce..bcabbd2948 100644 --- a/application/Espo/Core/Authentication/Helper/UserFinder.php +++ b/application/Espo/Core/Authentication/Helper/UserFinder.php @@ -39,18 +39,35 @@ class UserFinder public function __construct(private EntityManager $entityManager) {} - public function find(string $username, string $hash): ?User + public function find(string $username): ?User { return $this->entityManager ->getRDBRepositoryByClass(User::class) ->where([ 'userName' => $username, - 'password' => $hash, 'type!=' => [User::TYPE_API, User::TYPE_SYSTEM], ]) ->findOne(); } + public function findByIdAndHash(string $username, string $id, ?string $hash): ?User + { + $where = [ + 'userName' => $username, + 'id' => $id, + 'type!=' => [User::TYPE_API, User::TYPE_SYSTEM], + ]; + + if ($hash) { + $where['password'] = $hash; + } + + return $this->entityManager + ->getRDBRepositoryByClass(User::class) + ->where($where) + ->findOne(); + } + public function findApiHmac(string $apiKey): ?User { return $this->entityManager diff --git a/application/Espo/Core/Authentication/Ldap/LdapLogin.php b/application/Espo/Core/Authentication/Ldap/LdapLogin.php index d285eac846..eb3e181da7 100644 --- a/application/Espo/Core/Authentication/Ldap/LdapLogin.php +++ b/application/Espo/Core/Authentication/Ldap/LdapLogin.php @@ -55,6 +55,7 @@ use Espo\Entities\User; use Exception; use Laminas\Ldap\Exception\LdapException; use Laminas\Ldap\Ldap; +use SensitiveParameter; /** * @noinspection PhpUnused @@ -313,18 +314,25 @@ class LdapLogin implements Login ->findOne(); } - private function adminLogin(string $username, string $password): ?User + private function adminLogin(string $username, #[SensitiveParameter] string $password): ?User { - $hash = $this->passwordHash->hash($password); - - return $this->entityManager + $user = $this->entityManager ->getRDBRepository(User::ENTITY_TYPE) ->where([ 'userName' => $username, - 'password' => $hash, 'type' => [User::TYPE_ADMIN, User::TYPE_SUPER_ADMIN], ]) ->findOne(); + + if (!$user) { + return null; + } + + if (!$this->passwordHash->verify($password, $user->get('password'))) { + return null; + } + + return $user; } /** diff --git a/application/Espo/Core/Authentication/Login/Data.php b/application/Espo/Core/Authentication/Login/Data.php index c422e51b97..cd8c462037 100644 --- a/application/Espo/Core/Authentication/Login/Data.php +++ b/application/Espo/Core/Authentication/Login/Data.php @@ -30,6 +30,7 @@ namespace Espo\Core\Authentication\Login; use Espo\Core\Authentication\AuthToken\AuthToken; +use SensitiveParameter; /** * Login data to be passed to the 'login' method. @@ -40,8 +41,11 @@ class Data private ?string $password; private ?AuthToken $authToken; - public function __construct(?string $username, ?string $password, ?AuthToken $authToken = null) - { + public function __construct( + ?string $username, + #[SensitiveParameter] ?string $password, + ?AuthToken $authToken = null + ) { $this->username = $username; $this->password = $password; $this->authToken = $authToken; diff --git a/application/Espo/Core/Authentication/Logins/Espo.php b/application/Espo/Core/Authentication/Logins/Espo.php index 007a312187..6e73345ab7 100644 --- a/application/Espo/Core/Authentication/Logins/Espo.php +++ b/application/Espo/Core/Authentication/Logins/Espo.php @@ -37,8 +37,6 @@ use Espo\Core\Authentication\Result; use Espo\Core\Authentication\Result\FailReason; use Espo\Core\Utils\PasswordHash; -use RuntimeException; - class Espo implements Login { public const NAME = 'Espo'; @@ -62,16 +60,16 @@ class Espo implements Login return Result::fail(FailReason::NO_PASSWORD); } - $hash = $authToken ? - $authToken->getHash() : - $this->passwordHash->hash($password); + if ($authToken) { + $user = $this->userFinder->findByIdAndHash($username, $authToken->getUserId(), $authToken->getHash()); + } else { + $user = $this->userFinder->find($username); - if (!$hash) { - throw new RuntimeException("No hash."); + if ($user && !$this->passwordHash->verify($password, $user->get('password'))) { + $user = null; + } } - $user = $this->userFinder->find($username, $hash); - if (!$user) { return Result::fail(FailReason::WRONG_CREDENTIALS); } diff --git a/application/Espo/Core/Formula/Functions/PasswordGroup/HashType.php b/application/Espo/Core/Formula/Functions/PasswordGroup/HashType.php index 7a07d11cde..8573638e9f 100644 --- a/application/Espo/Core/Formula/Functions/PasswordGroup/HashType.php +++ b/application/Espo/Core/Formula/Functions/PasswordGroup/HashType.php @@ -29,13 +29,12 @@ namespace Espo\Core\Formula\Functions\PasswordGroup; -use Espo\Core\Formula\{ - Functions\BaseFunction, - ArgumentList, - Processor, -}; +use Espo\Core\Formula\ArgumentList; +use Espo\Core\Formula\Functions\BaseFunction; +use Espo\Core\Formula\Processor; use Espo\Core\Utils\PasswordHash; +use SensitiveParameter; class HashType extends BaseFunction { @@ -47,7 +46,7 @@ class HashType extends BaseFunction $this->passwordHash = $passwordHash; } - public function process(ArgumentList $args) + public function process(#[SensitiveParameter] ArgumentList $args) { if (count($args) < 1) { $this->throwTooFewArguments(); @@ -59,8 +58,6 @@ class HashType extends BaseFunction $this->throwBadArgumentType(1, 'string'); } - $hash = $this->passwordHash->hash($password); - - return $hash; + return $this->passwordHash->hash($password); } } diff --git a/application/Espo/Core/Utils/PasswordHash.php b/application/Espo/Core/Utils/PasswordHash.php index 2d92157350..0996d66075 100644 --- a/application/Espo/Core/Utils/PasswordHash.php +++ b/application/Espo/Core/Utils/PasswordHash.php @@ -30,6 +30,7 @@ namespace Espo\Core\Utils; use RuntimeException; +use SensitiveParameter; class PasswordHash { @@ -44,19 +45,25 @@ class PasswordHash /** * Hash a password. */ - public function hash(string $password, bool $useMd5 = true): string + public function hash(#[SensitiveParameter] string $password): string { $salt = $this->getSalt(); - if ($useMd5) { - $password = md5($password); - } + $password = md5($password); $hash = crypt($password, $salt); return str_replace($salt, '', $hash); } + public function verify( + #[SensitiveParameter] string $password, + #[SensitiveParameter] string $hash + ): bool { + + return $this->hash($password) === $hash; + } + /** * Get a salt from the config and normalize it. */