This commit is contained in:
Eldad Fux
2025-03-17 12:39:35 +01:00
parent f618a8b563
commit 1ce84f1650
6 changed files with 38 additions and 31 deletions
+9 -5
View File
@@ -38,6 +38,7 @@ use MaxMind\Db\Reader;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Audit\Audit as EventAudit;
use Utopia\Auth\Proofs\Password as ProofsPassword;
use Utopia\Auth\Store;
use Utopia\Config\Config;
use Utopia\Database\Database;
@@ -364,7 +365,9 @@ App::post('/v1/account')
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]);
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
$password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
$proof = new ProofsPassword();
$hash = $proof->hash($password);
try {
$userId = $userId == 'unique()' ? ID::unique() : $userId;
$user->setAttributes([
@@ -377,7 +380,7 @@ App::post('/v1/account')
'email' => $email,
'emailVerification' => false,
'status' => true,
'password' => $password,
'password' => $hash,
'passwordHistory' => $passwordHistory > 0 ? [$password] : [],
'passwordUpdate' => DateTime::now(),
'hash' => Auth::DEFAULT_ALGO,
@@ -942,7 +945,7 @@ App::post('/v1/account/sessions/email')
// Re-hash if not using recommended algo
if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) {
$user
->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS))
->setAttribute('password', (new ProofsPassword())->hash($password))
->setAttribute('hash', Auth::DEFAULT_ALGO)
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
$dbForProject->updateDocument('users', $user->getId(), $user);
@@ -2930,13 +2933,14 @@ App::patch('/v1/account/email')
->inject('queueForEvents')
->inject('project')
->inject('hooks')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) {
->inject('proofForPassword')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
if (
!empty($passwordUpdate) &&
!Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))
!$proofForPassword->verify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))
) { // Double check user password
throw new Exception(Exception::USER_INVALID_CREDENTIALS);
}
+7 -4
View File
@@ -27,6 +27,7 @@ use MaxMind\Db\Reader;
use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Auth\Proofs\Password;
use Utopia\Auth\Store;
use Utopia\Config\Config;
use Utopia\Database\Database;
@@ -469,7 +470,8 @@ App::post('/v1/teams/:teamId/memberships')
->inject('timelimit')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
->inject('proofForPassword')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword) {
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -542,6 +544,7 @@ App::post('/v1/teams/:teamId/memberships')
try {
$userId = ID::unique();
$hash = $proofForPassword->hash($proofForPassword->generate());
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
'$id' => $userId,
'$permissions' => [
@@ -555,9 +558,9 @@ App::post('/v1/teams/:teamId/memberships')
'emailVerification' => false,
'status' => true,
// TODO: Set password empty?
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
'hash' => Auth::DEFAULT_ALGO,
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
'password' => $hash,
'hash' => $proofForPassword->getHash()->getName(),
'hashOptions' => $proofForPassword->getHash()->getOptions(),
/**
* Set the password update time to 0 for users created using
* team invite and OAuth to allow password updates without an
+20 -1
View File
@@ -51,6 +51,9 @@ use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Auth\Proofs\Code;
use Utopia\Auth\Proofs\Password;
use Utopia\Auth\Proofs\Token;
use Utopia\Auth\Store;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Cache\Adapter\Sharding;
@@ -1976,6 +1979,22 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key
return Key::decode($project, $key);
}, ['request', 'project']);
App::setResource('store', function () {
App::setResource('store', function (): Store {
return new Store();
});
App::setResource('proofForPassword', function (): Password {
return new Password();
});
App::setResource('proofForToken', function (): Token {
return new Token();
});
App::setResource('proofForCode', function (): Code {
return new Code();
});
-14
View File
@@ -237,20 +237,6 @@ class Auth
}
}
/**
* Password Generator.
*
* Generate random password string
*
* @param int $length
*
* @return string
*/
public static function passwordGenerator(int $length = 20): string
{
return \bin2hex(\random_bytes($length));
}
/**
* Token Generator.
*
+2 -1
View File
@@ -6,6 +6,7 @@ use Appwrite\Auth\Auth;
use Appwrite\Docker\Compose;
use Appwrite\Docker\Env;
use Appwrite\Utopia\View;
use Utopia\Auth\Proofs\Password;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Platform\Action;
@@ -162,7 +163,7 @@ class Install extends Action
}
if ($var['filter'] === 'password') {
$input[$var['name']] = Auth::passwordGenerator();
$input[$var['name']] = (new Password())->generate();
continue;
}
}
-6
View File
@@ -163,12 +163,6 @@ class AuthTest extends TestCase
$this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md8'));
}
public function testPasswordGenerator(): void
{
$this->assertEquals(\mb_strlen(Auth::passwordGenerator()), 40);
$this->assertEquals(\mb_strlen(Auth::passwordGenerator(5)), 10);
}
public function testTokenGenerator(): void
{
$this->assertEquals(\strlen(Auth::tokenGenerator()), 256);