mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
fix(usermanager): Don't throw when checking if a too long user id is an existing user
Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
parent
85d2ee5262
commit
775ca882f3
3 changed files with 55 additions and 3 deletions
|
|
@ -24,6 +24,8 @@ use OCP\User\Events\UserDeletedEvent;
|
|||
* @template-implements IEventListener<UserChangedEvent|UserDeletedEvent>
|
||||
*/
|
||||
class DisplayNameCache implements IEventListener {
|
||||
/** @see \OC\Config\UserConfig::USER_MAX_LENGTH */
|
||||
public const MAX_USERID_LENGTH = 64;
|
||||
private array $cache = [];
|
||||
private ICache $memCache;
|
||||
private IUserManager $userManager;
|
||||
|
|
@ -37,6 +39,11 @@ class DisplayNameCache implements IEventListener {
|
|||
if (isset($this->cache[$userId])) {
|
||||
return $this->cache[$userId];
|
||||
}
|
||||
|
||||
if (strlen($userId) > self::MAX_USERID_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$displayName = $this->memCache->get($userId);
|
||||
if ($displayName) {
|
||||
$this->cache[$userId] = $displayName;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ use Psr\Log\LoggerInterface;
|
|||
* @package OC\User
|
||||
*/
|
||||
class Manager extends PublicEmitter implements IUserManager {
|
||||
/** @see \OC\Config\UserConfig::USER_MAX_LENGTH */
|
||||
public const MAX_USERID_LENGTH = 64;
|
||||
|
||||
/**
|
||||
* @var UserInterface[] $backends
|
||||
*/
|
||||
|
|
@ -118,6 +121,10 @@ class Manager extends PublicEmitter implements IUserManager {
|
|||
return $this->cachedUsers[$uid];
|
||||
}
|
||||
|
||||
if (strlen($uid) > self::MAX_USERID_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cachedBackend = $this->cache->get(sha1($uid));
|
||||
if ($cachedBackend !== null && isset($this->backends[$cachedBackend])) {
|
||||
// Cache has the info of the user backend already, so ask that one directly
|
||||
|
|
@ -177,6 +184,10 @@ class Manager extends PublicEmitter implements IUserManager {
|
|||
* @return bool
|
||||
*/
|
||||
public function userExists($uid) {
|
||||
if (strlen($uid) > self::MAX_USERID_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = $this->get($uid);
|
||||
return ($user !== null);
|
||||
}
|
||||
|
|
@ -692,14 +703,14 @@ class Manager extends PublicEmitter implements IUserManager {
|
|||
public function validateUserId(string $uid, bool $checkDataDirectory = false): void {
|
||||
$l = Server::get(IFactory::class)->get('lib');
|
||||
|
||||
// Check the name for bad characters
|
||||
// Check the ID for bad characters
|
||||
// Allowed are: "a-z", "A-Z", "0-9", spaces and "_.@-'"
|
||||
if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
|
||||
throw new \InvalidArgumentException($l->t('Only the following characters are allowed in an Login:'
|
||||
. ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'));
|
||||
}
|
||||
|
||||
// No empty username
|
||||
// No empty user ID
|
||||
if (trim($uid) === '') {
|
||||
throw new \InvalidArgumentException($l->t('A valid Login must be provided'));
|
||||
}
|
||||
|
|
@ -709,11 +720,16 @@ class Manager extends PublicEmitter implements IUserManager {
|
|||
throw new \InvalidArgumentException($l->t('Login contains whitespace at the beginning or at the end'));
|
||||
}
|
||||
|
||||
// Username only consists of 1 or 2 dots (directory traversal)
|
||||
// User ID only consists of 1 or 2 dots (directory traversal)
|
||||
if ($uid === '.' || $uid === '..') {
|
||||
throw new \InvalidArgumentException($l->t('Login must not consist of dots only'));
|
||||
}
|
||||
|
||||
// User ID is too long
|
||||
if (strlen($uid) > self::MAX_USERID_LENGTH) {
|
||||
throw new \InvalidArgumentException($l->t('Login is too long'));
|
||||
}
|
||||
|
||||
if (!$this->verifyUid($uid, $checkDataDirectory)) {
|
||||
throw new \InvalidArgumentException($l->t('Login is invalid because files already exist for this user'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,20 @@ class ManagerTest extends TestCase {
|
|||
$this->assertTrue($manager->userExists('foo'));
|
||||
}
|
||||
|
||||
public function testUserExistsTooLong(): void {
|
||||
/** @var \Test\Util\User\Dummy|MockObject $backend */
|
||||
$backend = $this->createMock(\Test\Util\User\Dummy::class);
|
||||
$backend->expects($this->never())
|
||||
->method('userExists')
|
||||
->with($this->equalTo('foo'))
|
||||
->willReturn(true);
|
||||
|
||||
$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
|
||||
$manager->registerBackend($backend);
|
||||
|
||||
$this->assertFalse($manager->userExists('foo' . str_repeat('a', 62)));
|
||||
}
|
||||
|
||||
public function testUserExistsSingleBackendNotExists(): void {
|
||||
/**
|
||||
* @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
|
||||
|
|
@ -230,6 +244,20 @@ class ManagerTest extends TestCase {
|
|||
$this->assertEquals(null, $manager->get('foo'));
|
||||
}
|
||||
|
||||
public function testGetTooLong(): void {
|
||||
/** @var \Test\Util\User\Dummy|MockObject $backend */
|
||||
$backend = $this->createMock(\Test\Util\User\Dummy::class);
|
||||
$backend->expects($this->never())
|
||||
->method('userExists')
|
||||
->with($this->equalTo('foo'))
|
||||
->willReturn(false);
|
||||
|
||||
$manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger);
|
||||
$manager->registerBackend($backend);
|
||||
|
||||
$this->assertEquals(null, $manager->get('foo' . str_repeat('a', 62)));
|
||||
}
|
||||
|
||||
public function testGetOneBackendDoNotTranslateLoginNames(): void {
|
||||
/**
|
||||
* @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend
|
||||
|
|
@ -333,6 +361,7 @@ class ManagerTest extends TestCase {
|
|||
['..', 'foo', 'Username must not consist of dots only'],
|
||||
['.test', '', 'A valid password must be provided'],
|
||||
['test', '', 'A valid password must be provided'],
|
||||
['test' . str_repeat('a', 61), '', 'Login is too long'],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue