Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(auth): Run token bulk update statements in atomic transaction #37676

Merged
merged 1 commit into from
Apr 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 55 additions & 51 deletions lib/private/Authentication/Token/PublicKeyTokenProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -327,18 +327,20 @@ public function setPassword(IToken $token, string $tokenId, string $password) {
throw new InvalidTokenException("Invalid token type");
}

// When changing passwords all temp tokens are deleted
$this->mapper->deleteTempToken($token);

// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($token->getUID());
$hashedPassword = $this->hashPassword($password);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($hashedPassword);
$this->updateToken($t);
}
$this->atomic(function () use ($password, $token) {
// When changing passwords all temp tokens are deleted
$this->mapper->deleteTempToken($token);

// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($token->getUID());
$hashedPassword = $this->hashPassword($password);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($hashedPassword);
$this->updateToken($t);
}
}, $this->db);
}

private function hashPassword(string $password): string {
Expand Down Expand Up @@ -489,49 +491,51 @@ public function updatePasswords(string $uid, string $password) {
return;
}

// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($uid);
$newPasswordHash = null;

/**
* - true: The password hash could not be verified anymore
* and the token needs to be updated with the newly encrypted password
* - false: The hash could still be verified
* - missing: The hash needs to be verified
*/
$hashNeedsUpdate = [];

foreach ($tokens as $t) {
if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) {
if ($t->getPasswordHash() === null) {
$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
} elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
} else {
$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false;
$this->atomic(function () use ($password, $uid) {
// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($uid);
$newPasswordHash = null;

/**
* - true: The password hash could not be verified anymore
* and the token needs to be updated with the newly encrypted password
* - false: The hash could still be verified
* - missing: The hash needs to be verified
*/
$hashNeedsUpdate = [];

foreach ($tokens as $t) {
if (!isset($hashNeedsUpdate[$t->getPasswordHash()])) {
if ($t->getPasswordHash() === null) {
$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
} elseif (!$this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = true;
} else {
$hashNeedsUpdate[$t->getPasswordHash() ?: ''] = false;
}
}
}
$needsUpdating = $hashNeedsUpdate[$t->getPasswordHash() ?: ''] ?? true;

if ($needsUpdating) {
if ($newPasswordHash === null) {
$newPasswordHash = $this->hashPassword($password);
$needsUpdating = $hashNeedsUpdate[$t->getPasswordHash() ?: ''] ?? true;

if ($needsUpdating) {
if ($newPasswordHash === null) {
$newPasswordHash = $this->hashPassword($password);
}

$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($newPasswordHash);
$t->setPasswordInvalid(false);
$this->updateToken($t);
}

$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($newPasswordHash);
$t->setPasswordInvalid(false);
$this->updateToken($t);
}
}

// If password hashes are different we update them all to be equal so
// that the next execution only needs to verify once
if (count($hashNeedsUpdate) > 1) {
$newPasswordHash = $this->hashPassword($password);
$this->mapper->updateHashesForUser($uid, $newPasswordHash);
}
// If password hashes are different we update them all to be equal so
// that the next execution only needs to verify once
if (count($hashNeedsUpdate) > 1) {
$newPasswordHash = $this->hashPassword($password);
$this->mapper->updateHashesForUser($uid, $newPasswordHash);
}
}, $this->db);
}

private function logOpensslError() {
Expand Down