mirror of https://github.com/nextcloud/server
PublickKeyTokenProvider: Fix password update routine with password hash
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
This commit is contained in:
parent
b37a4950e4
commit
adfe367106
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2022 Marcel Klehr <mklehr@gmx.net>
|
||||
*
|
||||
* @author Marcel Klehr <mklehr@gmx.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\DB\Types;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version25000Date20220905140840 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
$authTokenTable = $schema->getTable('authtoken');
|
||||
if (!$authTokenTable->hasColumn('password_hash')) {
|
||||
$authTokenTable->addColumn('password_hash', Types::STRING, [
|
||||
'notnull' => false,
|
||||
'length' => 255,
|
||||
]);
|
||||
return $schema;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1075,6 +1075,7 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version24000Date20220425072957' => $baseDir . '/core/Migrations/Version24000Date20220425072957.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20220515204012' => $baseDir . '/core/Migrations/Version25000Date20220515204012.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20220602190540' => $baseDir . '/core/Migrations/Version25000Date20220602190540.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20220905140840' => $baseDir . '/core/Migrations/Version25000Date20220905140840.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20221007010957' => $baseDir . '/core/Migrations/Version25000Date20221007010957.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
|
||||
|
|
|
@ -1108,6 +1108,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Migrations\\Version24000Date20220425072957' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220425072957.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20220515204012' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220515204012.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20220602190540' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220602190540.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20220905140840' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220905140840.php',
|
||||
'OC\\Core\\Migrations\\Version25000Date20221007010957' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20221007010957.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
|
||||
|
|
|
@ -45,6 +45,8 @@ use OCP\AppFramework\Db\Entity;
|
|||
* @method void setPublicKey(string $key)
|
||||
* @method void setVersion(int $version)
|
||||
* @method bool getPasswordInvalid()
|
||||
* @method string getPasswordHash()
|
||||
* @method setPasswordHash(string $hash)
|
||||
*/
|
||||
class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
|
||||
public const VERSION = 2;
|
||||
|
@ -58,6 +60,9 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
|
|||
/** @var string encrypted user password */
|
||||
protected $password;
|
||||
|
||||
/** @var string hashed user password */
|
||||
protected $passwordHash;
|
||||
|
||||
/** @var string token name (e.g. browser/OS) */
|
||||
protected $name;
|
||||
|
||||
|
@ -98,6 +103,7 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken {
|
|||
$this->addType('uid', 'string');
|
||||
$this->addType('loginName', 'string');
|
||||
$this->addType('password', 'string');
|
||||
$this->addType('passwordHash', 'string');
|
||||
$this->addType('name', 'string');
|
||||
$this->addType('token', 'string');
|
||||
$this->addType('type', 'int');
|
||||
|
|
|
@ -41,6 +41,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
|
|||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Security\ICrypto;
|
||||
use OCP\Security\IHasher;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class PublicKeyTokenProvider implements IProvider {
|
||||
|
@ -66,12 +67,15 @@ class PublicKeyTokenProvider implements IProvider {
|
|||
/** @var CappedMemoryCache */
|
||||
private $cache;
|
||||
|
||||
private IHasher $hasher;
|
||||
|
||||
public function __construct(PublicKeyTokenMapper $mapper,
|
||||
ICrypto $crypto,
|
||||
IConfig $config,
|
||||
IDBConnection $db,
|
||||
LoggerInterface $logger,
|
||||
ITimeFactory $time) {
|
||||
ITimeFactory $time,
|
||||
IHasher $hasher) {
|
||||
$this->mapper = $mapper;
|
||||
$this->crypto = $crypto;
|
||||
$this->config = $config;
|
||||
|
@ -80,6 +84,7 @@ class PublicKeyTokenProvider implements IProvider {
|
|||
$this->time = $time;
|
||||
|
||||
$this->cache = new CappedMemoryCache();
|
||||
$this->hasher = $hasher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -286,10 +291,15 @@ class PublicKeyTokenProvider implements IProvider {
|
|||
foreach ($tokens as $t) {
|
||||
$publicKey = $t->getPublicKey();
|
||||
$t->setPassword($this->encryptPassword($password, $publicKey));
|
||||
$t->setPasswordHash($this->hashPassword($password));
|
||||
$this->updateToken($t);
|
||||
}
|
||||
}
|
||||
|
||||
private function hashPassword(string $password): string {
|
||||
return $this->hasher->hash(sha1($password) . $password);
|
||||
}
|
||||
|
||||
public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
|
||||
$this->cache->clear();
|
||||
|
||||
|
@ -401,6 +411,7 @@ class PublicKeyTokenProvider implements IProvider {
|
|||
throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
|
||||
}
|
||||
$dbToken->setPassword($this->encryptPassword($password, $publicKey));
|
||||
$dbToken->setPasswordHash($this->hashPassword($password));
|
||||
}
|
||||
|
||||
$dbToken->setName($name);
|
||||
|
@ -435,11 +446,12 @@ class PublicKeyTokenProvider implements IProvider {
|
|||
|
||||
// Update the password for all tokens
|
||||
$tokens = $this->mapper->getTokenByUser($uid);
|
||||
$passwordHash = $this->hashPassword($password);
|
||||
foreach ($tokens as $t) {
|
||||
$publicKey = $t->getPublicKey();
|
||||
$encryptedPassword = $this->encryptPassword($password, $publicKey);
|
||||
if ($t->getPassword() !== $encryptedPassword) {
|
||||
$t->setPassword($encryptedPassword);
|
||||
if ($t->getPasswordHash() === null || $this->hasher->verify(sha1($password) . $password, $t->getPasswordHash())) {
|
||||
$t->setPassword($this->encryptPassword($password, $publicKey));
|
||||
$t->setPasswordHash($passwordHash);
|
||||
$t->setPasswordInvalid(false);
|
||||
$this->updateToken($t);
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ class PublicKeyTokenProviderTest extends TestCase {
|
|||
parent::setUp();
|
||||
|
||||
$this->mapper = $this->createMock(PublicKeyTokenMapper::class);
|
||||
$this->hasher = \OC::$server->getHasher();
|
||||
$this->crypto = \OC::$server->getCrypto();
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->config->method('getSystemValue')
|
||||
|
@ -87,6 +88,7 @@ class PublicKeyTokenProviderTest extends TestCase {
|
|||
$this->db,
|
||||
$this->logger,
|
||||
$this->timeFactory,
|
||||
$this->hasher,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue