Skip to content

Commit 3a97dbf

Browse files
authored
Merge pull request #46123 from nextcloud/feat/user-password-hash
feat: Allow getting/setting the password hash of a user
2 parents 025a784 + c390ae9 commit 3a97dbf

7 files changed

Lines changed: 110 additions & 1 deletion

File tree

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@
799799
'OCP\\User\\Backend\\IGetHomeBackend' => $baseDir . '/lib/public/User/Backend/IGetHomeBackend.php',
800800
'OCP\\User\\Backend\\IGetRealUIDBackend' => $baseDir . '/lib/public/User/Backend/IGetRealUIDBackend.php',
801801
'OCP\\User\\Backend\\IPasswordConfirmationBackend' => $baseDir . '/lib/public/User/Backend/IPasswordConfirmationBackend.php',
802+
'OCP\\User\\Backend\\IPasswordHashBackend' => $baseDir . '/lib/public/User/Backend/IPasswordHashBackend.php',
802803
'OCP\\User\\Backend\\IProvideAvatarBackend' => $baseDir . '/lib/public/User/Backend/IProvideAvatarBackend.php',
803804
'OCP\\User\\Backend\\IProvideEnabledStateBackend' => $baseDir . '/lib/public/User/Backend/IProvideEnabledStateBackend.php',
804805
'OCP\\User\\Backend\\ISearchKnownUsersBackend' => $baseDir . '/lib/public/User/Backend/ISearchKnownUsersBackend.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
832832
'OCP\\User\\Backend\\IGetHomeBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/IGetHomeBackend.php',
833833
'OCP\\User\\Backend\\IGetRealUIDBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/IGetRealUIDBackend.php',
834834
'OCP\\User\\Backend\\IPasswordConfirmationBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/IPasswordConfirmationBackend.php',
835+
'OCP\\User\\Backend\\IPasswordHashBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/IPasswordHashBackend.php',
835836
'OCP\\User\\Backend\\IProvideAvatarBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/IProvideAvatarBackend.php',
836837
'OCP\\User\\Backend\\IProvideEnabledStateBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/IProvideEnabledStateBackend.php',
837838
'OCP\\User\\Backend\\ISearchKnownUsersBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/ISearchKnownUsersBackend.php',

lib/private/User/Database.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
namespace OC\User;
1010

11+
use InvalidArgumentException;
1112
use OCP\AppFramework\Db\TTransactional;
1213
use OCP\Cache\CappedMemoryCache;
1314
use OCP\EventDispatcher\IEventDispatcher;
@@ -21,6 +22,7 @@
2122
use OCP\User\Backend\IGetDisplayNameBackend;
2223
use OCP\User\Backend\IGetHomeBackend;
2324
use OCP\User\Backend\IGetRealUIDBackend;
25+
use OCP\User\Backend\IPasswordHashBackend;
2426
use OCP\User\Backend\ISearchKnownUsersBackend;
2527
use OCP\User\Backend\ISetDisplayNameBackend;
2628
use OCP\User\Backend\ISetPasswordBackend;
@@ -37,7 +39,8 @@ class Database extends ABackend implements
3739
IGetHomeBackend,
3840
ICountUsersBackend,
3941
ISearchKnownUsersBackend,
40-
IGetRealUIDBackend {
42+
IGetRealUIDBackend,
43+
IPasswordHashBackend {
4144
/** @var CappedMemoryCache */
4245
private $cache;
4346

@@ -176,6 +179,40 @@ public function setPassword(string $uid, string $password): bool {
176179
return false;
177180
}
178181

182+
public function getPasswordHash(string $userId): ?string {
183+
$this->fixDI();
184+
if (!$this->userExists($userId)) {
185+
return null;
186+
}
187+
if (!empty($this->cache[$userId]['password'])) {
188+
return $this->cache[$userId]['password'];
189+
}
190+
$qb = $this->dbConn->getQueryBuilder();
191+
$qb->select('password')
192+
->from($this->table)
193+
->where($qb->expr()->eq('uid_lower', $qb->createNamedParameter(mb_strtolower($userId))));
194+
/** @var false|string $hash */
195+
$hash = $qb->executeQuery()->fetchOne();
196+
if ($hash === false) {
197+
return null;
198+
}
199+
$this->cache[$userId]['password'] = $hash;
200+
return $hash;
201+
}
202+
203+
public function setPasswordHash(string $userId, string $passwordHash): bool {
204+
if (!\OCP\Server::get(IHasher::class)->validate($passwordHash)) {
205+
throw new InvalidArgumentException();
206+
}
207+
$this->fixDI();
208+
$result = $this->updatePassword($userId, $passwordHash);
209+
if (!$result) {
210+
return false;
211+
}
212+
$this->cache[$userId]['password'] = $passwordHash;
213+
return true;
214+
}
215+
179216
/**
180217
* Set display name
181218
*

lib/private/User/LazyUser.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ public function setPassword($password, $recoveryPassword = null) {
7373
return $this->getUser()->setPassword($password, $recoveryPassword);
7474
}
7575

76+
public function getPasswordHash(): ?string {
77+
return $this->getUser()->getPasswordHash();
78+
}
79+
80+
public function setPasswordHash(string $passwordHash): bool {
81+
return $this->getUser()->setPasswordHash($passwordHash);
82+
}
83+
7684
public function getHome() {
7785
return $this->getUser()->getHome();
7886
}

lib/private/User/User.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use OCP\IUserBackend;
2626
use OCP\Notification\IManager as INotificationManager;
2727
use OCP\User\Backend\IGetHomeBackend;
28+
use OCP\User\Backend\IPasswordHashBackend;
2829
use OCP\User\Backend\IProvideAvatarBackend;
2930
use OCP\User\Backend\IProvideEnabledStateBackend;
3031
use OCP\User\Backend\ISetDisplayNameBackend;
@@ -319,6 +320,20 @@ public function setPassword($password, $recoveryPassword = null) {
319320
}
320321
}
321322

323+
public function getPasswordHash(): ?string {
324+
if (!($this->backend instanceof IPasswordHashBackend)) {
325+
return null;
326+
}
327+
return $this->backend->getPasswordHash($this->uid);
328+
}
329+
330+
public function setPasswordHash(string $passwordHash): bool {
331+
if (!($this->backend instanceof IPasswordHashBackend)) {
332+
return false;
333+
}
334+
return $this->backend->setPasswordHash($this->uid, $passwordHash);
335+
}
336+
322337
/**
323338
* get the users home folder to mount
324339
*

lib/public/IUser.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ public function delete();
7676
*/
7777
public function setPassword($password, $recoveryPassword = null);
7878

79+
/**
80+
* Get the password hash of the user
81+
*
82+
* @return ?string the password hash hashed by `\OCP\Security\IHasher::hash()`
83+
* @since 30.0.0
84+
*/
85+
public function getPasswordHash(): ?string;
86+
87+
/**
88+
* Set the password hash of the user
89+
*
90+
* @param string $passwordHash the password hash hashed by `\OCP\Security\IHasher::hash()`
91+
* @throws InvalidArgumentException when `$passwordHash` is not a valid hash
92+
* @since 30.0.0
93+
*/
94+
public function setPasswordHash(string $passwordHash): bool;
95+
7996
/**
8097
* get the users home folder to mount
8198
*
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCP\User\Backend;
11+
12+
use InvalidArgumentException;
13+
14+
/**
15+
* @since 30.0.0
16+
*/
17+
interface IPasswordHashBackend {
18+
/**
19+
* @return ?string the password hash hashed by `\OCP\Security\IHasher::hash()`
20+
* @since 30.0.0
21+
*/
22+
public function getPasswordHash(string $userId): ?string;
23+
24+
/**
25+
* @param string $passwordHash the password hash hashed by `\OCP\Security\IHasher::hash()`
26+
* @throws InvalidArgumentException when `$passwordHash` is not a valid hash
27+
* @since 30.0.0
28+
*/
29+
public function setPasswordHash(string $userId, string $passwordHash): bool;
30+
}

0 commit comments

Comments
 (0)