diff options
Diffstat (limited to 'tests/lib/User')
-rw-r--r-- | tests/lib/User/AvailabilityCoordinatorTest.php | 220 | ||||
-rw-r--r-- | tests/lib/User/AvatarUserDummy.php | 22 | ||||
-rw-r--r-- | tests/lib/User/Backend.php | 28 | ||||
-rw-r--r-- | tests/lib/User/DatabaseTest.php | 59 | ||||
-rw-r--r-- | tests/lib/User/Dummy.php | 22 | ||||
-rw-r--r-- | tests/lib/User/ManagerTest.php | 283 | ||||
-rw-r--r-- | tests/lib/User/SessionTest.php | 466 | ||||
-rw-r--r-- | tests/lib/User/UserTest.php | 344 |
8 files changed, 1022 insertions, 422 deletions
diff --git a/tests/lib/User/AvailabilityCoordinatorTest.php b/tests/lib/User/AvailabilityCoordinatorTest.php new file mode 100644 index 00000000000..09c1528912b --- /dev/null +++ b/tests/lib/User/AvailabilityCoordinatorTest.php @@ -0,0 +1,220 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\User; + +use OC\User\AvailabilityCoordinator; +use OC\User\OutOfOfficeData; +use OCA\DAV\CalDAV\TimezoneService; +use OCA\DAV\Db\Absence; +use OCA\DAV\Service\AbsenceService; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IConfig; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class AvailabilityCoordinatorTest extends TestCase { + private AvailabilityCoordinator $availabilityCoordinator; + private ICacheFactory $cacheFactory; + private ICache $cache; + private IConfig|MockObject $config; + private AbsenceService $absenceService; + private LoggerInterface $logger; + private MockObject|TimezoneService $timezoneService; + + protected function setUp(): void { + parent::setUp(); + + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cache = $this->createMock(ICache::class); + $this->absenceService = $this->createMock(AbsenceService::class); + $this->config = $this->createMock(IConfig::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->timezoneService = $this->createMock(TimezoneService::class); + + $this->cacheFactory->expects(self::once()) + ->method('createLocal') + ->willReturn($this->cache); + + $this->availabilityCoordinator = new AvailabilityCoordinator( + $this->cacheFactory, + $this->config, + $this->absenceService, + $this->logger, + $this->timezoneService, + ); + } + + public function testIsEnabled(): void { + $this->config->expects(self::once()) + ->method('getAppValue') + ->with('dav', 'hide_absence_settings', 'no') + ->willReturn('no'); + + $isEnabled = $this->availabilityCoordinator->isEnabled(); + + self::assertTrue($isEnabled); + } + + public function testGetOutOfOfficeDataInEffect(): void { + $absence = new Absence(); + $absence->setId(420); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $absence->setLastDay('2023-10-08'); + $absence->setStatus('Vacation'); + $absence->setMessage('On vacation'); + $absence->setReplacementUserId('batman'); + $absence->setReplacementUserDisplayName('Bruce Wayne'); + $this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); + + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('user'); + + $this->cache->expects(self::exactly(2)) + ->method('get') + ->willReturnOnConsecutiveCalls(null, null); + $this->absenceService->expects(self::once()) + ->method('getAbsence') + ->with($user->getUID()) + ->willReturn($absence); + + $calls = [ + [$user->getUID() . '_timezone', 'Europe/Berlin', 3600], + [$user->getUID(), '{"id":"420","startDate":1696111200,"endDate":1696802340,"shortMessage":"Vacation","message":"On vacation","replacementUserId":"batman","replacementUserDisplayName":"Bruce Wayne"}', 300], + ]; + $this->cache->expects(self::exactly(2)) + ->method('set') + ->willReturnCallback(static function () use (&$calls): void { + $expected = array_shift($calls); + self::assertEquals($expected, func_get_args()); + }); + + $expected = new OutOfOfficeData( + '420', + $user, + 1696111200, + 1696802340, + 'Vacation', + 'On vacation', + 'batman', + 'Bruce Wayne', + ); + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertEquals($expected, $actual); + } + + public function testGetOutOfOfficeDataCachedAll(): void { + $absence = new Absence(); + $absence->setId(420); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $absence->setLastDay('2023-10-08'); + $absence->setStatus('Vacation'); + $absence->setMessage('On vacation'); + $absence->setReplacementUserId('batman'); + $absence->setReplacementUserDisplayName('Bruce Wayne'); + + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('user'); + + $this->cache->expects(self::exactly(2)) + ->method('get') + ->willReturnOnConsecutiveCalls('UTC', '{"id":"420","startDate":1696118400,"endDate":1696809540,"shortMessage":"Vacation","message":"On vacation","replacementUserId":"batman","replacementUserDisplayName":"Bruce Wayne"}'); + $this->absenceService->expects(self::never()) + ->method('getAbsence'); + $this->cache->expects(self::exactly(1)) + ->method('set'); + + $expected = new OutOfOfficeData( + '420', + $user, + 1696118400, + 1696809540, + 'Vacation', + 'On vacation', + 'batman', + 'Bruce Wayne' + ); + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertEquals($expected, $actual); + } + + public function testGetOutOfOfficeDataNoData(): void { + $absence = new Absence(); + $absence->setId(420); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $absence->setLastDay('2023-10-08'); + $absence->setStatus('Vacation'); + $absence->setMessage('On vacation'); + + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('user'); + + $this->cache->expects(self::exactly(2)) + ->method('get') + ->willReturnOnConsecutiveCalls('UTC', null); + $this->absenceService->expects(self::once()) + ->method('getAbsence') + ->willReturn(null); + $this->cache->expects(self::never()) + ->method('set'); + + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertNull($actual); + } + + public function testGetOutOfOfficeDataWithInvalidCachedData(): void { + $absence = new Absence(); + $absence->setId(420); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $absence->setLastDay('2023-10-08'); + $absence->setStatus('Vacation'); + $absence->setMessage('On vacation'); + $absence->setReplacementUserId('batman'); + $absence->setReplacementUserDisplayName('Bruce Wayne'); + $this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); + + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('user'); + + $this->cache->expects(self::exactly(2)) + ->method('get') + ->willReturnOnConsecutiveCalls('UTC', '{"id":"420",}'); + $this->absenceService->expects(self::once()) + ->method('getAbsence') + ->with('user') + ->willReturn($absence); + $this->cache->expects(self::once()) + ->method('set') + ->with('user', '{"id":"420","startDate":1696118400,"endDate":1696809540,"shortMessage":"Vacation","message":"On vacation","replacementUserId":"batman","replacementUserDisplayName":"Bruce Wayne"}', 300); + + $expected = new OutOfOfficeData( + '420', + $user, + 1696118400, + 1696809540, + 'Vacation', + 'On vacation', + 'batman', + 'Bruce Wayne' + ); + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertEquals($expected, $actual); + } +} diff --git a/tests/lib/User/AvatarUserDummy.php b/tests/lib/User/AvatarUserDummy.php index 87e1cfc5f83..001dabd24c6 100644 --- a/tests/lib/User/AvatarUserDummy.php +++ b/tests/lib/User/AvatarUserDummy.php @@ -1,23 +1,9 @@ <?php + /** - * ownCloud - * - * @author Arthur Schiwon - * @copyright 2013 Arthur Schiwon blizzz@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; diff --git a/tests/lib/User/Backend.php b/tests/lib/User/Backend.php index fd87da72364..dc5b245fa06 100644 --- a/tests/lib/User/Backend.php +++ b/tests/lib/User/Backend.php @@ -1,23 +1,9 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; @@ -47,7 +33,7 @@ abstract class Backend extends \Test\TestCase { return $this->getUniqueID('test_'); } - public function testAddRemove() { + public function testAddRemove(): void { //get the number of groups we start with, in case there are exising groups $startCount = count($this->backend->getUsers()); @@ -71,7 +57,7 @@ abstract class Backend extends \Test\TestCase { $this->assertFalse((array_search($name2, $this->backend->getUsers()) !== false)); } - public function testLogin() { + public function testLogin(): void { $name1 = $this->getUser(); $name2 = $this->getUser(); @@ -99,7 +85,7 @@ abstract class Backend extends \Test\TestCase { $this->assertFalse($this->backend->checkPassword($name2, 'newpass1')); } - public function testSearch() { + public function testSearch(): void { $name1 = 'foobarbaz'; $name2 = 'bazbarfoo'; $name3 = 'notme'; diff --git a/tests/lib/User/DatabaseTest.php b/tests/lib/User/DatabaseTest.php index 49b691cf9bc..33101173c0a 100644 --- a/tests/lib/User/DatabaseTest.php +++ b/tests/lib/User/DatabaseTest.php @@ -1,34 +1,20 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; +use OC\User\Database; use OC\User\User; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\HintException; use OCP\Security\Events\ValidatePasswordPolicyEvent; use PHPUnit\Framework\MockObject\MockObject; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class DatabaseTest @@ -41,6 +27,9 @@ class DatabaseTest extends Backend { /** @var IEventDispatcher|MockObject */ private $eventDispatcher; + /** @var \OC\User\Database */ + protected $backend; + public function getUser() { $user = parent::getUser(); $this->users[] = $user; @@ -52,7 +41,7 @@ class DatabaseTest extends Backend { $this->eventDispatcher = $this->createMock(IEventDispatcher::class); - $this->backend = new \OC\User\Database($this->eventDispatcher); + $this->backend = new Database($this->eventDispatcher); } protected function tearDown(): void { @@ -65,13 +54,13 @@ class DatabaseTest extends Backend { parent::tearDown(); } - public function testVerifyPasswordEvent() { + public function testVerifyPasswordEvent(): void { $user = $this->getUser(); $this->backend->createUser($user, 'pass1'); $this->eventDispatcher->expects($this->once())->method('dispatchTyped') ->willReturnCallback( - function (Event $event) { + function (Event $event): void { $this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event); /** @var ValidatePasswordPolicyEvent $event */ $this->assertSame('newpass', $event->getPassword()); @@ -83,8 +72,8 @@ class DatabaseTest extends Backend { } - public function testVerifyPasswordEventFail() { - $this->expectException(\OCP\HintException::class); + public function testVerifyPasswordEventFail(): void { + $this->expectException(HintException::class); $this->expectExceptionMessage('password change failed'); $user = $this->getUser(); @@ -92,7 +81,7 @@ class DatabaseTest extends Backend { $this->eventDispatcher->expects($this->once())->method('dispatchTyped') ->willReturnCallback( - function (Event $event) { + function (Event $event): void { $this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event); /** @var ValidatePasswordPolicyEvent $event */ $this->assertSame('newpass', $event->getPassword()); @@ -104,14 +93,14 @@ class DatabaseTest extends Backend { $this->assertSame($user, $this->backend->checkPassword($user, 'newpass')); } - public function testCreateUserInvalidatesCache() { + public function testCreateUserInvalidatesCache(): void { $user1 = $this->getUniqueID('test_'); $this->assertFalse($this->backend->userExists($user1)); $this->backend->createUser($user1, 'pw'); $this->assertTrue($this->backend->userExists($user1)); } - public function testDeleteUserInvalidatesCache() { + public function testDeleteUserInvalidatesCache(): void { $user1 = $this->getUniqueID('test_'); $this->backend->createUser($user1, 'pw'); $this->assertTrue($this->backend->userExists($user1)); @@ -121,7 +110,7 @@ class DatabaseTest extends Backend { $this->assertTrue($this->backend->userExists($user1)); } - public function testSearch() { + public function testSearch(): void { parent::testSearch(); $user1 = $this->getUser(); @@ -130,8 +119,8 @@ class DatabaseTest extends Backend { $user2 = $this->getUser(); $this->backend->createUser($user2, 'pass1'); - $user1Obj = new User($user1, $this->backend, $this->createMock(EventDispatcherInterface::class)); - $user2Obj = new User($user2, $this->backend, $this->createMock(EventDispatcherInterface::class)); + $user1Obj = new User($user1, $this->backend, $this->createMock(IEventDispatcher::class)); + $user2Obj = new User($user2, $this->backend, $this->createMock(IEventDispatcher::class)); $emailAddr1 = "$user1@nextcloud.com"; $emailAddr2 = "$user2@nextcloud.com"; @@ -155,4 +144,14 @@ class DatabaseTest extends Backend { $result = $this->backend->getDisplayNames('@nextcloud.COM'); $this->assertCount(2, $result); } + + public function testUserCount(): void { + $base = $this->backend->countUsers() ?: 0; + $users = $this->backend->getUsers(); + self::assertEquals($base, count($users)); + + $user = $this->getUser(); + $this->backend->createUser($user, $user); + self::assertEquals($base + 1, $this->backend->countUsers()); + } } diff --git a/tests/lib/User/Dummy.php b/tests/lib/User/Dummy.php index a9e4a2936a7..ec5be8ec60a 100644 --- a/tests/lib/User/Dummy.php +++ b/tests/lib/User/Dummy.php @@ -1,23 +1,9 @@ <?php + /** - * ownCloud - * - * @author Robin Appelman - * @copyright 2012 Robin Appelman icewind@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; diff --git a/tests/lib/User/ManagerTest.php b/tests/lib/User/ManagerTest.php index c8c1430d583..d5872787d0a 100644 --- a/tests/lib/User/ManagerTest.php +++ b/tests/lib/User/ManagerTest.php @@ -1,23 +1,26 @@ <?php /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; use OC\AllConfig; +use OC\USER\BACKEND; use OC\User\Database; use OC\User\Manager; +use OC\User\User; use OCP\EventDispatcher\IEventDispatcher; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IUser; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use OCP\IUserManager; +use OCP\Server; +use Psr\Log\LoggerInterface; use Test\TestCase; /** @@ -28,35 +31,33 @@ use Test\TestCase; * @package Test\User */ class ManagerTest extends TestCase { - /** @var IConfig */ private $config; - /** @var EventDispatcherInterface */ - private $oldDispatcher; /** @var IEventDispatcher */ private $eventDispatcher; /** @var ICacheFactory */ private $cacheFactory; /** @var ICache */ private $cache; + /** @var LoggerInterface */ + private $logger; protected function setUp(): void { parent::setUp(); $this->config = $this->createMock(IConfig::class); - $this->oldDispatcher = $this->createMock(EventDispatcherInterface::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->cache = $this->createMock(ICache::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->cacheFactory->method('createDistributed') - ->with('user_backend_map') ->willReturn($this->cache); } - public function testGetBackends() { + public function testGetBackends(): void { $userDummyBackend = $this->createMock(\Test\Util\User\Dummy::class); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($userDummyBackend); $this->assertEquals([$userDummyBackend], $manager->getBackends()); $dummyDatabaseBackend = $this->createMock(Database::class); @@ -65,7 +66,7 @@ class ManagerTest extends TestCase { } - public function testUserExistsSingleBackendExists() { + public function testUserExistsSingleBackendExists(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -75,13 +76,27 @@ class ManagerTest extends TestCase { ->with($this->equalTo('foo')) ->willReturn(true); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertTrue($manager->userExists('foo')); } - public function testUserExistsSingleBackendNotExists() { + 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 */ @@ -91,19 +106,19 @@ class ManagerTest extends TestCase { ->with($this->equalTo('foo')) ->willReturn(false); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertFalse($manager->userExists('foo')); } - public function testUserExistsNoBackends() { - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + public function testUserExistsNoBackends(): void { + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $this->assertFalse($manager->userExists('foo')); } - public function testUserExistsTwoBackendsSecondExists() { + public function testUserExistsTwoBackendsSecondExists(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1 */ @@ -122,14 +137,14 @@ class ManagerTest extends TestCase { ->with($this->equalTo('foo')) ->willReturn(true); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend1); $manager->registerBackend($backend2); $this->assertTrue($manager->userExists('foo')); } - public function testUserExistsTwoBackendsFirstExists() { + public function testUserExistsTwoBackendsFirstExists(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1 */ @@ -146,14 +161,14 @@ class ManagerTest extends TestCase { $backend2->expects($this->never()) ->method('userExists'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend1); $manager->registerBackend($backend2); $this->assertTrue($manager->userExists('foo')); } - public function testCheckPassword() { + public function testCheckPassword(): void { /** * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -166,21 +181,21 @@ class ManagerTest extends TestCase { $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { - if ($actions === \OC\USER\BACKEND::CHECK_PASSWORD) { + if ($actions === BACKEND::CHECK_PASSWORD) { return true; } else { return false; } }); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $user = $manager->checkPassword('foo', 'bar'); - $this->assertTrue($user instanceof \OC\User\User); + $this->assertTrue($user instanceof User); } - public function testCheckPasswordNotSupported() { + public function testCheckPasswordNotSupported(): void { /** * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -192,13 +207,13 @@ class ManagerTest extends TestCase { ->method('implementsActions') ->willReturn(false); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertFalse($manager->checkPassword('foo', 'bar')); } - public function testGetOneBackendExists() { + public function testGetOneBackendExists(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -210,13 +225,13 @@ class ManagerTest extends TestCase { $backend->expects($this->never()) ->method('loginName2UserName'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertEquals('foo', $manager->get('foo')->getUID()); } - public function testGetOneBackendNotExists() { + public function testGetOneBackendNotExists(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -226,13 +241,27 @@ class ManagerTest extends TestCase { ->with($this->equalTo('foo')) ->willReturn(false); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertEquals(null, $manager->get('foo')); } - public function testGetOneBackendDoNotTranslateLoginNames() { + 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 */ @@ -244,13 +273,13 @@ class ManagerTest extends TestCase { $backend->expects($this->never()) ->method('loginName2UserName'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertEquals('bLeNdEr', $manager->get('bLeNdEr')->getUID()); } - public function testSearchOneBackend() { + public function testSearchOneBackend(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -262,7 +291,7 @@ class ManagerTest extends TestCase { $backend->expects($this->never()) ->method('loginName2UserName'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $result = $manager->search('fo'); @@ -273,7 +302,7 @@ class ManagerTest extends TestCase { $this->assertEquals('foo', array_shift($result)->getUID()); } - public function testSearchTwoBackendLimitOffset() { + public function testSearchTwoBackendLimitOffset(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend1 */ @@ -296,7 +325,7 @@ class ManagerTest extends TestCase { $backend2->expects($this->never()) ->method('loginName2UserName'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend1); $manager->registerBackend($backend2); @@ -307,26 +336,26 @@ class ManagerTest extends TestCase { $this->assertEquals('foo3', array_shift($result)->getUID()); } - public function dataCreateUserInvalid() { + public static function dataCreateUserInvalid(): array { return [ ['te?st', 'foo', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\tst", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\nst", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\rst", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\0st", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\x0Bst", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\xe2st", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\x80st", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ["te\x8bst", '', 'Only the following characters are allowed in a username:' - . ' "a-z", "A-Z", "0-9", and "_.@-\'"'], + . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"'], ['', 'foo', 'A valid username must be provided'], [' ', 'foo', 'A valid username must be provided'], [' test', 'foo', 'Username contains whitespace at the beginning or at the end'], @@ -335,13 +364,12 @@ 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'], ]; } - /** - * @dataProvider dataCreateUserInvalid - */ - public function testCreateUserInvalid($uid, $password, $exception) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataCreateUserInvalid')] + public function testCreateUserInvalid($uid, $password, $exception): void { /** @var \Test\Util\User\Dummy|\PHPUnit\Framework\MockObject\MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -350,14 +378,14 @@ class ManagerTest extends TestCase { ->willReturn(true); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->expectException(\InvalidArgumentException::class, $exception); $manager->createUser($uid, $password); } - public function testCreateUserSingleBackendNotExists() { + public function testCreateUserSingleBackendNotExists(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -377,7 +405,7 @@ class ManagerTest extends TestCase { $backend->expects($this->never()) ->method('loginName2UserName'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $user = $manager->createUser('foo', 'bar'); @@ -385,7 +413,7 @@ class ManagerTest extends TestCase { } - public function testCreateUserSingleBackendExists() { + public function testCreateUserSingleBackendExists(): void { $this->expectException(\Exception::class); /** @@ -404,13 +432,13 @@ class ManagerTest extends TestCase { ->with($this->equalTo('foo')) ->willReturn(true); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $manager->createUser('foo', 'bar'); } - public function testCreateUserSingleBackendNotSupported() { + public function testCreateUserSingleBackendNotSupported(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -425,22 +453,22 @@ class ManagerTest extends TestCase { $backend->expects($this->never()) ->method('userExists'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $this->assertFalse($manager->createUser('foo', 'bar')); } - public function testCreateUserNoBackends() { - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + public function testCreateUserNoBackends(): void { + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $this->assertFalse($manager->createUser('foo', 'bar')); } - public function testCreateUserFromBackendWithBackendError() { + public function testCreateUserFromBackendWithBackendError(): void { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Could not create user'); + $this->expectExceptionMessage('Could not create account'); /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject $config */ $config = $this->createMock(IConfig::class); @@ -452,12 +480,12 @@ class ManagerTest extends TestCase { ->with('MyUid', 'MyPassword') ->willReturn(false); - $manager = new Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->createUserFromBackend('MyUid', 'MyPassword', $backend); } - public function testCreateUserTwoBackendExists() { + public function testCreateUserTwoBackendExists(): void { $this->expectException(\Exception::class); /** @@ -492,22 +520,22 @@ class ManagerTest extends TestCase { ->with($this->equalTo('foo')) ->willReturn(true); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend1); $manager->registerBackend($backend2); $manager->createUser('foo', 'bar'); } - public function testCountUsersNoBackend() { - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + public function testCountUsersNoBackend(): void { + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $result = $manager->countUsers(); $this->assertTrue(is_array($result)); $this->assertTrue(empty($result)); } - public function testCountUsersOneBackend() { + public function testCountUsersOneBackend(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -518,14 +546,14 @@ class ManagerTest extends TestCase { $backend->expects($this->once()) ->method('implementsActions') - ->with(\OC\USER\BACKEND::COUNT_USERS) + ->with(BACKEND::COUNT_USERS) ->willReturn(true); $backend->expects($this->once()) ->method('getBackendName') ->willReturn('Mock_Test_Util_User_Dummy'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $result = $manager->countUsers(); @@ -536,7 +564,7 @@ class ManagerTest extends TestCase { $this->assertEquals(7, $users); } - public function testCountUsersTwoBackends() { + public function testCountUsersTwoBackends(): void { /** * @var \Test\Util\User\Dummy | \PHPUnit\Framework\MockObject\MockObject $backend */ @@ -547,7 +575,7 @@ class ManagerTest extends TestCase { $backend1->expects($this->once()) ->method('implementsActions') - ->with(\OC\USER\BACKEND::COUNT_USERS) + ->with(BACKEND::COUNT_USERS) ->willReturn(true); $backend1->expects($this->once()) ->method('getBackendName') @@ -560,13 +588,13 @@ class ManagerTest extends TestCase { $backend2->expects($this->once()) ->method('implementsActions') - ->with(\OC\USER\BACKEND::COUNT_USERS) + ->with(BACKEND::COUNT_USERS) ->willReturn(true); $backend2->expects($this->once()) ->method('getBackendName') ->willReturn('Mock_Test_Util_User_Dummy'); - $manager = new \OC\User\Manager($this->config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($this->config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend1); $manager->registerBackend($backend2); @@ -581,8 +609,8 @@ class ManagerTest extends TestCase { $this->assertEquals(7 + 16, $users); } - public function testCountUsersOnlyDisabled() { - $manager = \OC::$server->getUserManager(); + public function testCountUsersOnlyDisabled(): void { + $manager = Server::get(IUserManager::class); // count other users in the db before adding our own $countBefore = $manager->countDisabledUsers(); @@ -606,10 +634,10 @@ class ManagerTest extends TestCase { $user4->delete(); } - public function testCountUsersOnlySeen() { - $manager = \OC::$server->getUserManager(); + public function testCountUsersOnlySeen(): void { + $manager = Server::get(IUserManager::class); // count other users in the db before adding our own - $countBefore = $manager->countUsers(true); + $countBefore = $manager->countSeenUsers(); //Add test users $user1 = $manager->createUser('testseencount1', 'testseencount1'); @@ -623,7 +651,7 @@ class ManagerTest extends TestCase { $user4 = $manager->createUser('testseencount4', 'testseencount4'); $user4->updateLastLoginTimestamp(); - $this->assertEquals($countBefore + 3, $manager->countUsers(true)); + $this->assertEquals($countBefore + 3, $manager->countSeenUsers()); //cleanup $user1->delete(); @@ -632,11 +660,11 @@ class ManagerTest extends TestCase { $user4->delete(); } - public function testCallForSeenUsers() { - $manager = \OC::$server->getUserManager(); + public function testCallForSeenUsers(): void { + $manager = Server::get(IUserManager::class); // count other users in the db before adding our own $count = 0; - $function = function (IUser $user) use (&$count) { + $function = function (IUser $user) use (&$count): void { $count++; }; $manager->callForAllUsers($function, '', true); @@ -666,7 +694,67 @@ class ManagerTest extends TestCase { $user4->delete(); } - public function testDeleteUser() { + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testRecentlyActive(): void { + $config = Server::get(IConfig::class); + $manager = Server::get(IUserManager::class); + + // Create some users + $now = (string)time(); + $user1 = $manager->createUser('test_active_1', 'test_active_1'); + $config->setUserValue('test_active_1', 'login', 'lastLogin', $now); + $user1->setDisplayName('test active 1'); + $user1->setSystemEMailAddress('roger@active.com'); + + $user2 = $manager->createUser('TEST_ACTIVE_2_FRED', 'TEST_ACTIVE_2'); + $config->setUserValue('TEST_ACTIVE_2_FRED', 'login', 'lastLogin', $now); + $user2->setDisplayName('TEST ACTIVE 2 UPPER'); + $user2->setSystemEMailAddress('Fred@Active.Com'); + + $user3 = $manager->createUser('test_active_3', 'test_active_3'); + $config->setUserValue('test_active_3', 'login', 'lastLogin', $now + 1); + $user3->setDisplayName('test active 3'); + + $user4 = $manager->createUser('test_active_4', 'test_active_4'); + $config->setUserValue('test_active_4', 'login', 'lastLogin', $now); + $user4->setDisplayName('Test Active 4'); + + $user5 = $manager->createUser('test_inactive_1', 'test_inactive_1'); + $user5->setDisplayName('Test Inactive 1'); + $user2->setSystemEMailAddress('jeanne@Active.Com'); + + // Search recently active + // - No search, case-insensitive order + $users = $manager->getLastLoggedInUsers(4); + $this->assertEquals(['test_active_3', 'test_active_1', 'TEST_ACTIVE_2_FRED', 'test_active_4'], $users); + // - Search, case-insensitive order + $users = $manager->getLastLoggedInUsers(search: 'act'); + $this->assertEquals(['test_active_3', 'test_active_1', 'TEST_ACTIVE_2_FRED', 'test_active_4'], $users); + // - No search with offset + $users = $manager->getLastLoggedInUsers(2, 2); + $this->assertEquals(['TEST_ACTIVE_2_FRED', 'test_active_4'], $users); + // - Case insensitive search (email) + $users = $manager->getLastLoggedInUsers(search: 'active.com'); + $this->assertEquals(['test_active_1', 'TEST_ACTIVE_2_FRED'], $users); + // - Case insensitive search (display name) + $users = $manager->getLastLoggedInUsers(search: 'upper'); + $this->assertEquals(['TEST_ACTIVE_2_FRED'], $users); + // - Case insensitive search (uid) + $users = $manager->getLastLoggedInUsers(search: 'fred'); + $this->assertEquals(['TEST_ACTIVE_2_FRED'], $users); + + // Delete users and config keys + $user1->delete(); + $user2->delete(); + $user3->delete(); + $user4->delete(); + $user5->delete(); + } + + public function testDeleteUser(): void { $config = $this->getMockBuilder(AllConfig::class) ->disableOriginalConstructor() ->getMock(); @@ -679,7 +767,7 @@ class ManagerTest extends TestCase { ->method('getAppValue') ->willReturnArgument(2); - $manager = new \OC\User\Manager($config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $backend = new \Test\Util\User\Dummy(); $manager->registerBackend($backend); @@ -689,31 +777,26 @@ class ManagerTest extends TestCase { $this->assertFalse($manager->userExists('foo')); } - public function testGetByEmail() { + public function testGetByEmail(): void { $config = $this->getMockBuilder(AllConfig::class) ->disableOriginalConstructor() ->getMock(); $config - ->expects($this->at(0)) + ->expects($this->once()) ->method('getUsersForUserValueCaseInsensitive') ->with('settings', 'email', 'test@example.com') ->willReturn(['uid1', 'uid99', 'uid2']); $backend = $this->createMock(\Test\Util\User\Dummy::class); - $backend->expects($this->at(0)) + $backend->expects($this->exactly(3)) ->method('userExists') - ->with($this->equalTo('uid1')) - ->willReturn(true); - $backend->expects($this->at(1)) - ->method('userExists') - ->with($this->equalTo('uid99')) - ->willReturn(false); - $backend->expects($this->at(2)) - ->method('userExists') - ->with($this->equalTo('uid2')) - ->willReturn(true); + ->willReturnMap([ + ['uid1', true], + ['uid99', false], + ['uid2', true] + ]); - $manager = new \OC\User\Manager($config, $this->oldDispatcher, $this->cacheFactory, $this->eventDispatcher); + $manager = new \OC\User\Manager($config, $this->cacheFactory, $this->eventDispatcher, $this->logger); $manager->registerBackend($backend); $users = $manager->getByEmail('test@example.com'); diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 0e199e5d5b5..50c449559a0 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -1,19 +1,22 @@ <?php + /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; use OC\AppFramework\Http\Request; +use OC\Authentication\Events\LoginFailed; use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; -use OC\Security\Bruteforce\Throttler; +use OC\Authentication\Token\PublicKeyToken; +use OC\Security\CSRF\CsrfTokenManager; use OC\Session\Memory; use OC\User\LoginException; use OC\User\Manager; @@ -29,12 +32,14 @@ use OCP\IRequestId; use OCP\ISession; use OCP\IUser; use OCP\Lockdown\ILockdownManager; +use OCP\Security\Bruteforce\IThrottler; use OCP\Security\ISecureRandom; use OCP\User\Events\PostLoginEvent; +use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use OC\Security\CSRF\CsrfTokenManager; +use function array_diff; +use function get_class_methods; /** * @group DB @@ -43,9 +48,11 @@ use OC\Security\CSRF\CsrfTokenManager; class SessionTest extends \Test\TestCase { /** @var ITimeFactory|MockObject */ private $timeFactory; + /** @var IProvider|MockObject */ + private $tokenProvider; /** @var IConfig|MockObject */ private $config; - /** @var Throttler|MockObject */ + /** @var IThrottler|MockObject */ private $throttler; /** @var ISecureRandom|MockObject */ private $random; @@ -71,7 +78,7 @@ class SessionTest extends \Test\TestCase { ->willReturn(10000); $this->tokenProvider = $this->createMock(IProvider::class); $this->config = $this->createMock(IConfig::class); - $this->throttler = $this->createMock(Throttler::class); + $this->throttler = $this->createMock(IThrottler::class); $this->random = $this->createMock(ISecureRandom::class); $this->manager = $this->createMock(Manager::class); $this->session = $this->createMock(ISession::class); @@ -90,7 +97,7 @@ class SessionTest extends \Test\TestCase { $this->logger, $this->dispatcher ]) - ->setMethods([ + ->onlyMethods([ 'setMagicInCookie', ]) ->getMock(); @@ -98,36 +105,34 @@ class SessionTest extends \Test\TestCase { \OC_User::setIncognitoMode(false); } - public function isLoggedInData() { + public static function isLoggedInData(): array { return [ [true], [false], ]; } - /** - * @dataProvider isLoggedInData - */ - public function testIsLoggedIn($isLoggedIn) { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('isLoggedInData')] + public function testIsLoggedIn($isLoggedIn): void { + $session = $this->createMock(Memory::class); $manager = $this->createMock(Manager::class); $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods([ + ->onlyMethods([ 'getUser' ]) ->getMock(); - $user = new User('sepp', null, $this->createMock(EventDispatcherInterface::class)); + $user = new User('sepp', null, $this->createMock(IEventDispatcher::class)); $userSession->expects($this->once()) ->method('getUser') ->willReturn($isLoggedIn ? $user : null); $this->assertEquals($isLoggedIn, $userSession->isLoggedIn()); } - public function testSetUser() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testSetUser(): void { + $session = $this->createMock(Memory::class); $session->expects($this->once()) ->method('set') ->with('user_id', 'foo'); @@ -145,14 +150,14 @@ class SessionTest extends \Test\TestCase { $userSession->setUser($user); } - public function testLoginValidPasswordEnabled() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testLoginValidPasswordEnabled(): void { + $session = $this->createMock(Memory::class); $session->expects($this->once()) ->method('regenerateId'); $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('bar') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $session->expects($this->exactly(2)) ->method('set') ->with($this->callback(function ($key) { @@ -171,12 +176,12 @@ class SessionTest extends \Test\TestCase { //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); @@ -199,7 +204,7 @@ class SessionTest extends \Test\TestCase { $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods([ + ->onlyMethods([ 'prepareUserLogin' ]) ->getMock(); @@ -210,9 +215,9 @@ class SessionTest extends \Test\TestCase { ->method('dispatchTyped') ->with( $this->callback(function (PostLoginEvent $e) { - return $e->getUser()->getUID() === 'foo' && - $e->getPassword() === 'bar' && - $e->isTokenLogin() === false; + return $e->getUser()->getUID() === 'foo' + && $e->getPassword() === 'bar' + && $e->isTokenLogin() === false; }) ); @@ -221,10 +226,10 @@ class SessionTest extends \Test\TestCase { } - public function testLoginValidPasswordDisabled() { + public function testLoginValidPasswordDisabled(): void { $this->expectException(LoginException::class); - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + $session = $this->createMock(Memory::class); $session->expects($this->never()) ->method('set'); $session->expects($this->once()) @@ -232,18 +237,18 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('bar') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); @@ -266,18 +271,18 @@ class SessionTest extends \Test\TestCase { $userSession->login('foo', 'bar'); } - public function testLoginInvalidPassword() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testLoginInvalidPassword(): void { + $session = $this->createMock(Memory::class); $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -292,7 +297,7 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('bar') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $user->expects($this->never()) ->method('isEnabled'); @@ -310,8 +315,84 @@ class SessionTest extends \Test\TestCase { $userSession->login('foo', 'bar'); } - public function testLoginNonExisting() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testPasswordlessLoginNoLastCheckUpdate(): void { + $session = $this->createMock(Memory::class); + $managerMethods = get_class_methods(Manager::class); + // Keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class) + ->onlyMethods($mockedManagerMethods) + ->setConstructorArgs([ + $this->config, + $this->createMock(ICacheFactory::class), + $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), + ]) + ->getMock(); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + + $session->expects($this->never()) + ->method('set'); + $session->expects($this->once()) + ->method('regenerateId'); + $token = new PublicKeyToken(); + $token->setLoginName('foo'); + $token->setLastCheck(0); // Never + $token->setUid('foo'); + $this->tokenProvider + ->method('getPassword') + ->with($token) + ->willThrowException(new PasswordlessTokenException()); + $this->tokenProvider + ->method('getToken') + ->with('app-password') + ->willReturn($token); + $this->tokenProvider->expects(self::never()) + ->method('updateToken'); + + $userSession->login('foo', 'app-password'); + } + + public function testLoginLastCheckUpdate(): void { + $session = $this->createMock(Memory::class); + $managerMethods = get_class_methods(Manager::class); + // Keep following methods intact in order to ensure hooks are working + $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); + $manager = $this->getMockBuilder(Manager::class) + ->onlyMethods($mockedManagerMethods) + ->setConstructorArgs([ + $this->config, + $this->createMock(ICacheFactory::class), + $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), + ]) + ->getMock(); + $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); + + $session->expects($this->never()) + ->method('set'); + $session->expects($this->once()) + ->method('regenerateId'); + $token = new PublicKeyToken(); + $token->setLoginName('foo'); + $token->setLastCheck(0); // Never + $token->setUid('foo'); + $this->tokenProvider + ->method('getPassword') + ->with($token) + ->willReturn('secret'); + $this->tokenProvider + ->method('getToken') + ->with('app-password') + ->willReturn($token); + $this->tokenProvider->expects(self::once()) + ->method('updateToken'); + + $userSession->login('foo', 'app-password'); + } + + public function testLoginNonExisting(): void { + $session = $this->createMock(Memory::class); $manager = $this->createMock(Manager::class); $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher); @@ -322,7 +403,7 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('bar') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $manager->expects($this->once()) ->method('checkPasswordNoLogging') @@ -332,7 +413,7 @@ class SessionTest extends \Test\TestCase { $userSession->login('foo', 'bar'); } - public function testLogClientInNoTokenPasswordWith2fa() { + public function testLogClientInNoTokenPasswordWith2fa(): void { $this->expectException(PasswordLoginForbiddenException::class); $manager = $this->createMock(Manager::class); @@ -342,15 +423,15 @@ class SessionTest extends \Test\TestCase { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) + ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('doe') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $this->config->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueBool') ->with('token_auth_enforced', false) ->willReturn(true); $request @@ -359,7 +440,7 @@ class SessionTest extends \Test\TestCase { ->willReturn('192.168.0.1'); $this->throttler ->expects($this->once()) - ->method('sleepDelay') + ->method('sleepDelayOrThrowOnMax') ->with('192.168.0.1'); $this->throttler ->expects($this->any()) @@ -370,7 +451,7 @@ class SessionTest extends \Test\TestCase { $userSession->logClientIn('john', 'doe', $request, $this->throttler); } - public function testLogClientInUnexist() { + public function testLogClientInUnexist(): void { $manager = $this->createMock(Manager::class); $session = $this->createMock(ISession::class); $request = $this->createMock(IRequest::class); @@ -378,15 +459,15 @@ class SessionTest extends \Test\TestCase { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) + ->onlyMethods(['login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('doe') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $this->config->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueBool') ->with('token_auth_enforced', false) ->willReturn(false); $manager->method('getByEmail') @@ -396,7 +477,7 @@ class SessionTest extends \Test\TestCase { $this->assertFalse($userSession->logClientIn('unexist', 'doe', $request, $this->throttler)); } - public function testLogClientInWithTokenPassword() { + public function testLogClientInWithTokenPassword(): void { $manager = $this->createMock(Manager::class); $session = $this->createMock(ISession::class); $request = $this->createMock(IRequest::class); @@ -404,7 +485,7 @@ class SessionTest extends \Test\TestCase { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods(['isTokenPassword', 'login', 'supportsCookies', 'createSessionToken', 'getUser']) + ->onlyMethods(['isTokenPassword', 'login', 'supportsCookies', 'createSessionToken', 'getUser']) ->getMock(); $userSession->expects($this->once()) @@ -424,7 +505,7 @@ class SessionTest extends \Test\TestCase { ->willReturn('192.168.0.1'); $this->throttler ->expects($this->once()) - ->method('sleepDelay') + ->method('sleepDelayOrThrowOnMax') ->with('192.168.0.1'); $this->throttler ->expects($this->any()) @@ -436,7 +517,7 @@ class SessionTest extends \Test\TestCase { } - public function testLogClientInNoTokenPasswordNo2fa() { + public function testLogClientInNoTokenPasswordNo2fa(): void { $this->expectException(PasswordLoginForbiddenException::class); $manager = $this->createMock(Manager::class); @@ -446,15 +527,15 @@ class SessionTest extends \Test\TestCase { /** @var Session $userSession */ $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods(['login', 'isTwoFactorEnforced']) + ->onlyMethods(['login', 'isTwoFactorEnforced']) ->getMock(); $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('doe') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $this->config->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueBool') ->with('token_auth_enforced', false) ->willReturn(false); @@ -469,7 +550,7 @@ class SessionTest extends \Test\TestCase { ->willReturn('192.168.0.1'); $this->throttler ->expects($this->once()) - ->method('sleepDelay') + ->method('sleepDelayOrThrowOnMax') ->with('192.168.0.1'); $this->throttler ->expects($this->any()) @@ -480,23 +561,112 @@ class SessionTest extends \Test\TestCase { $userSession->logClientIn('john', 'doe', $request, $this->throttler); } - public function testRememberLoginValidToken() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testTryTokenLoginNoHeaderNoSessionCookie(): void { + $request = $this->createMock(IRequest::class); + $this->config->expects(self::once()) + ->method('getSystemValueString') + ->with('instanceid') + ->willReturn('abc123'); + $request->method('getHeader')->with('Authorization')->willReturn(''); + $request->method('getCookie')->with('abc123')->willReturn(null); + $this->tokenProvider->expects(self::never()) + ->method('getToken'); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertFalse($loginResult); + } + + public function testTryTokenLoginAuthorizationHeaderTokenNotFound(): void { + $request = $this->createMock(IRequest::class); + $request->method('getHeader')->with('Authorization')->willReturn('Bearer abcde-12345'); + $this->tokenProvider->expects(self::once()) + ->method('getToken') + ->with('abcde-12345') + ->willThrowException(new InvalidTokenException()); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertFalse($loginResult); + } + + public function testTryTokenLoginSessionIdTokenNotFound(): void { + $request = $this->createMock(IRequest::class); + $this->config->expects(self::once()) + ->method('getSystemValueString') + ->with('instanceid') + ->willReturn('abc123'); + $request->method('getHeader')->with('Authorization')->willReturn(''); + $request->method('getCookie')->with('abc123')->willReturn('abcde12345'); + $this->session->expects(self::once()) + ->method('getId') + ->willReturn('abcde12345'); + $this->tokenProvider->expects(self::once()) + ->method('getToken') + ->with('abcde12345') + ->willThrowException(new InvalidTokenException()); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertFalse($loginResult); + } + + public function testTryTokenLoginNotAnAppPassword(): void { + $request = $this->createMock(IRequest::class); + $this->config->expects(self::once()) + ->method('getSystemValueString') + ->with('instanceid') + ->willReturn('abc123'); + $request->method('getHeader')->with('Authorization')->willReturn(''); + $request->method('getCookie')->with('abc123')->willReturn('abcde12345'); + $this->session->expects(self::once()) + ->method('getId') + ->willReturn('abcde12345'); + $dbToken = new PublicKeyToken(); + $dbToken->setId(42); + $dbToken->setUid('johnny'); + $dbToken->setLoginName('johnny'); + $dbToken->setLastCheck(0); + $dbToken->setType(IToken::TEMPORARY_TOKEN); + $dbToken->setRemember(IToken::REMEMBER); + $this->tokenProvider->expects(self::any()) + ->method('getToken') + ->with('abcde12345') + ->willReturn($dbToken); + $this->session->method('set') + ->willReturnCallback(function ($key, $value): void { + if ($key === 'app_password') { + throw new ExpectationFailedException('app_password should not be set in session'); + } + }); + $user = $this->createMock(IUser::class); + $user->method('isEnabled')->willReturn(true); + $this->manager->method('get') + ->with('johnny') + ->willReturn($user); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertTrue($loginResult); + } + + public function testRememberLoginValidToken(): void { + $session = $this->createMock(Memory::class); $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() - ->setMethods(['setMagicInCookie', 'setLoginName']) + ->onlyMethods(['setMagicInCookie', 'setLoginName']) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) ->getMock(); @@ -554,7 +724,7 @@ class SessionTest extends \Test\TestCase { $setUID = false; $session ->method('set') - ->willReturnCallback(function ($k, $v) use (&$setUID) { + ->willReturnCallback(function ($k, $v) use (&$setUID): void { if ($k === 'user_id' && $v === 'foo') { $setUID = true; } @@ -570,23 +740,23 @@ class SessionTest extends \Test\TestCase { $this->assertTrue($granted); } - public function testRememberLoginInvalidSessionToken() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testRememberLoginInvalidSessionToken(): void { + $session = $this->createMock(Memory::class); $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() - ->setMethods(['setMagicInCookie']) + ->onlyMethods(['setMagicInCookie']) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) ->getMock(); @@ -617,7 +787,7 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider->expects($this->once()) ->method('renewSessionToken') ->with($oldSessionId, $sessionId) - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $user->expects($this->never()) ->method('getUID') @@ -635,23 +805,23 @@ class SessionTest extends \Test\TestCase { $this->assertFalse($granted); } - public function testRememberLoginInvalidToken() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testRememberLoginInvalidToken(): void { + $session = $this->createMock(Memory::class); $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() - ->setMethods(['setMagicInCookie']) + ->onlyMethods(['setMagicInCookie']) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) ->getMock(); @@ -688,23 +858,23 @@ class SessionTest extends \Test\TestCase { $this->assertFalse($granted); } - public function testRememberLoginInvalidUser() { - $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); + public function testRememberLoginInvalidUser(): void { + $session = $this->createMock(Memory::class); $managerMethods = get_class_methods(Manager::class); //keep following methods intact in order to ensure hooks are working $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']); $manager = $this->getMockBuilder(Manager::class) - ->setMethods($mockedManagerMethods) + ->onlyMethods($mockedManagerMethods) ->setConstructorArgs([ $this->config, - $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), + $this->createMock(LoggerInterface::class), ]) ->getMock(); $userSession = $this->getMockBuilder(Session::class) //override, otherwise tests will fail because of setcookie() - ->setMethods(['setMagicInCookie']) + ->onlyMethods(['setMagicInCookie']) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) ->getMock(); $token = 'goodToken'; @@ -734,10 +904,10 @@ class SessionTest extends \Test\TestCase { $this->assertFalse($granted); } - public function testActiveUserAfterSetSession() { + public function testActiveUserAfterSetSession(): void { $users = [ - 'foo' => new User('foo', null, $this->createMock(EventDispatcherInterface::class)), - 'bar' => new User('bar', null, $this->createMock(EventDispatcherInterface::class)) + 'foo' => new User('foo', null, $this->createMock(IEventDispatcher::class)), + 'bar' => new User('bar', null, $this->createMock(IEventDispatcher::class)) ]; $manager = $this->getMockBuilder(Manager::class) @@ -750,11 +920,11 @@ class SessionTest extends \Test\TestCase { return $users[$uid]; }); - $session = new Memory(''); + $session = new Memory(); $session->set('user_id', 'foo'); $userSession = $this->getMockBuilder(Session::class) ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) - ->setMethods([ + ->onlyMethods([ 'validateSession' ]) ->getMock(); @@ -763,13 +933,13 @@ class SessionTest extends \Test\TestCase { $this->assertEquals($users['foo'], $userSession->getUser()); - $session2 = new Memory(''); + $session2 = new Memory(); $session2->set('user_id', 'bar'); $userSession->setSession($session2); $this->assertEquals($users['bar'], $userSession->getUser()); } - public function testCreateSessionToken() { + public function testCreateSessionToken(): void { $manager = $this->createMock(Manager::class); $session = $this->createMock(ISession::class); $user = $this->createMock(IUser::class); @@ -801,7 +971,7 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider->expects($this->once()) ->method('getToken') ->with($password) - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $this->tokenProvider->expects($this->once()) ->method('generateToken') @@ -810,7 +980,7 @@ class SessionTest extends \Test\TestCase { $this->assertTrue($userSession->createSessionToken($request, $uid, $loginName, $password)); } - public function testCreateRememberedSessionToken() { + public function testCreateRememberedSessionToken(): void { $manager = $this->createMock(Manager::class); $session = $this->createMock(ISession::class); $user = $this->createMock(IUser::class); @@ -842,7 +1012,7 @@ class SessionTest extends \Test\TestCase { $this->tokenProvider->expects($this->once()) ->method('getToken') ->with($password) - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $this->tokenProvider->expects($this->once()) ->method('generateToken') @@ -851,7 +1021,7 @@ class SessionTest extends \Test\TestCase { $this->assertTrue($userSession->createSessionToken($request, $uid, $loginName, $password, true)); } - public function testCreateSessionTokenWithTokenPassword() { + public function testCreateSessionTokenWithTokenPassword(): void { $manager = $this->getMockBuilder(Manager::class) ->disableOriginalConstructor() ->getMock(); @@ -900,7 +1070,7 @@ class SessionTest extends \Test\TestCase { $this->assertTrue($userSession->createSessionToken($request, $uid, $loginName, $password)); } - public function testCreateSessionTokenWithNonExistentUser() { + public function testCreateSessionTokenWithNonExistentUser(): void { $manager = $this->getMockBuilder(Manager::class) ->disableOriginalConstructor() ->getMock(); @@ -920,7 +1090,7 @@ class SessionTest extends \Test\TestCase { $this->assertFalse($userSession->createSessionToken($request, $uid, $loginName, $password)); } - public function testCreateRememberMeToken() { + public function testCreateRememberMeToken(): void { $user = $this->createMock(IUser::class); $user ->expects($this->exactly(2)) @@ -943,7 +1113,7 @@ class SessionTest extends \Test\TestCase { $this->userSession->createRememberMeToken($user); } - public function testTryBasicAuthLoginValid() { + public function testTryBasicAuthLoginValid(): void { $request = $this->createMock(Request::class); $request->method('__get') ->willReturn([ @@ -959,7 +1129,7 @@ class SessionTest extends \Test\TestCase { $this->session ->method('set') - ->willReturnCallback(function ($k, $v) use (&$davAuthenticatedSet, &$lastPasswordConfirmSet) { + ->willReturnCallback(function ($k, $v) use (&$davAuthenticatedSet, &$lastPasswordConfirmSet): void { switch ($k) { case Auth::DAV_AUTHENTICATED: $davAuthenticatedSet = $v; @@ -984,7 +1154,7 @@ class SessionTest extends \Test\TestCase { $this->logger, $this->dispatcher ]) - ->setMethods([ + ->onlyMethods([ 'logClientIn', 'getUser', ]) @@ -1013,7 +1183,7 @@ class SessionTest extends \Test\TestCase { $this->assertSame(1000, $lastPasswordConfirmSet); } - public function testTryBasicAuthLoginNoLogin() { + public function testTryBasicAuthLoginNoLogin(): void { $request = $this->createMock(Request::class); $request->method('__get') ->willReturn([]); @@ -1036,7 +1206,7 @@ class SessionTest extends \Test\TestCase { $this->logger, $this->dispatcher ]) - ->setMethods([ + ->onlyMethods([ 'logClientIn', ]) ->getMock(); @@ -1048,11 +1218,107 @@ class SessionTest extends \Test\TestCase { $this->assertFalse($userSession->tryBasicAuthLogin($request, $this->throttler)); } - public function testUpdateTokens() { + public function testUpdateTokens(): void { $this->tokenProvider->expects($this->once()) ->method('updatePasswords') ->with('uid', 'pass'); $this->userSession->updateTokens('uid', 'pass'); } + + public function testLogClientInThrottlerUsername(): void { + $manager = $this->createMock(Manager::class); + $session = $this->createMock(ISession::class); + $request = $this->createMock(IRequest::class); + + /** @var Session $userSession */ + $userSession = $this->getMockBuilder(Session::class) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->onlyMethods(['isTokenPassword', 'login', 'supportsCookies', 'createSessionToken', 'getUser']) + ->getMock(); + + $userSession->expects($this->once()) + ->method('isTokenPassword') + ->willReturn(true); + $userSession->expects($this->once()) + ->method('login') + ->with('john', 'I-AM-AN-PASSWORD') + ->willReturn(false); + + $session->expects($this->never()) + ->method('set'); + $request + ->method('getRemoteAddress') + ->willReturn('192.168.0.1'); + $this->throttler + ->expects($this->exactly(2)) + ->method('sleepDelayOrThrowOnMax') + ->with('192.168.0.1'); + $this->throttler + ->expects($this->any()) + ->method('getDelay') + ->with('192.168.0.1') + ->willReturn(0); + + $this->throttler + ->expects($this->once()) + ->method('registerAttempt') + ->with('login', '192.168.0.1', ['user' => 'john']); + $this->dispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with(new LoginFailed('john', 'I-AM-AN-PASSWORD')); + + $this->assertFalse($userSession->logClientIn('john', 'I-AM-AN-PASSWORD', $request, $this->throttler)); + } + + public function testLogClientInThrottlerEmail(): void { + $manager = $this->createMock(Manager::class); + $session = $this->createMock(ISession::class); + $request = $this->createMock(IRequest::class); + + /** @var Session $userSession */ + $userSession = $this->getMockBuilder(Session::class) + ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher]) + ->onlyMethods(['isTokenPassword', 'login', 'supportsCookies', 'createSessionToken', 'getUser']) + ->getMock(); + + $userSession->expects($this->once()) + ->method('isTokenPassword') + ->willReturn(false); + $userSession->expects($this->once()) + ->method('login') + ->with('john@foo.bar', 'I-AM-AN-PASSWORD') + ->willReturn(false); + $manager + ->method('getByEmail') + ->with('john@foo.bar') + ->willReturn([]); + + $session->expects($this->never()) + ->method('set'); + $request + ->method('getRemoteAddress') + ->willReturn('192.168.0.1'); + $this->throttler + ->expects($this->exactly(2)) + ->method('sleepDelayOrThrowOnMax') + ->with('192.168.0.1'); + $this->throttler + ->expects($this->any()) + ->method('getDelay') + ->with('192.168.0.1') + ->willReturn(0); + + $this->throttler + ->expects($this->once()) + ->method('registerAttempt') + ->with('login', '192.168.0.1', ['user' => 'john@foo.bar']); + $this->dispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with(new LoginFailed('john@foo.bar', 'I-AM-AN-PASSWORD')); + + $this->assertFalse($userSession->logClientIn('john@foo.bar', 'I-AM-AN-PASSWORD', $request, $this->throttler)); + } } diff --git a/tests/lib/User/UserTest.php b/tests/lib/User/UserTest.php index 7fab7ececca..05056c92193 100644 --- a/tests/lib/User/UserTest.php +++ b/tests/lib/User/UserTest.php @@ -1,10 +1,9 @@ <?php /** - * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\User; @@ -12,16 +11,20 @@ namespace Test\User; use OC\AllConfig; use OC\Files\Mount\ObjectHomeMountProvider; use OC\Hooks\PublicEmitter; +use OC\User\Database; use OC\User\User; use OCP\Comments\ICommentsManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\FileInfo; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; +use OCP\IURLGenerator; use OCP\IUser; use OCP\Notification\IManager as INotificationManager; use OCP\Notification\INotification; +use OCP\Server; use OCP\UserInterface; use PHPUnit\Framework\MockObject\MockObject; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; /** @@ -32,18 +35,17 @@ use Test\TestCase; * @package Test\User */ class UserTest extends TestCase { - - /** @var EventDispatcherInterface|MockObject */ + /** @var IEventDispatcher|MockObject */ protected $dispatcher; protected function setUp(): void { parent::setUp(); - $this->dispatcher = $this->createMock(EventDispatcherInterface::class); + $this->dispatcher = Server::get(IEventDispatcher::class); } - public function testDisplayName() { + public function testDisplayName(): void { /** - * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var \OC\User\Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Backend::class); $backend->expects($this->once()) @@ -63,9 +65,9 @@ class UserTest extends TestCase { /** * if the display name contain whitespaces only, we expect the uid as result */ - public function testDisplayNameEmpty() { + public function testDisplayNameEmpty(): void { /** - * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var \OC\User\Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Backend::class); $backend->expects($this->once()) @@ -82,9 +84,9 @@ class UserTest extends TestCase { $this->assertEquals('foo', $user->getDisplayName()); } - public function testDisplayNameNotSupported() { + public function testDisplayNameNotSupported(): void { /** - * @var \OC\User\Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var \OC\User\Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Backend::class); $backend->expects($this->never()) @@ -99,9 +101,9 @@ class UserTest extends TestCase { $this->assertEquals('foo', $user->getDisplayName()); } - public function testSetPassword() { + public function testSetPassword(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -122,9 +124,9 @@ class UserTest extends TestCase { $this->assertTrue($user->setPassword('bar', '')); } - public function testSetPasswordNotSupported() { + public function testSetPasswordNotSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->never()) @@ -138,9 +140,9 @@ class UserTest extends TestCase { $this->assertFalse($user->setPassword('bar', '')); } - public function testChangeAvatarSupportedYes() { + public function testChangeAvatarSupportedYes(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(AvatarUserDummy::class); $backend->expects($this->once()) @@ -162,9 +164,9 @@ class UserTest extends TestCase { $this->assertTrue($user->canChangeAvatar()); } - public function testChangeAvatarSupportedNo() { + public function testChangeAvatarSupportedNo(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(AvatarUserDummy::class); $backend->expects($this->once()) @@ -186,9 +188,9 @@ class UserTest extends TestCase { $this->assertFalse($user->canChangeAvatar()); } - public function testChangeAvatarNotSupported() { + public function testChangeAvatarNotSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(AvatarUserDummy::class); $backend->expects($this->never()) @@ -202,9 +204,9 @@ class UserTest extends TestCase { $this->assertTrue($user->canChangeAvatar()); } - public function testDelete() { + public function testDelete(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -215,23 +217,22 @@ class UserTest extends TestCase { $this->assertTrue($user->delete()); } - public function testDeleteWithDifferentHome() { - + public function testDeleteWithDifferentHome(): void { /** @var ObjectHomeMountProvider $homeProvider */ - $homeProvider = \OC::$server->get(ObjectHomeMountProvider::class); + $homeProvider = Server::get(ObjectHomeMountProvider::class); $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('foo'); if ($homeProvider->getHomeMountForUser($user, $this->createMock(IStorageFactory::class)) !== null) { - $this->markTestSkipped("Skipping test for non local home storage"); + $this->markTestSkipped('Skipping test for non local home storage'); } /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); - $backend->expects($this->at(0)) + $backend->expects($this->once()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::GET_HOME) { @@ -244,12 +245,12 @@ class UserTest extends TestCase { // important: getHome MUST be called before deleteUser because // once the user is deleted, getHome implementations might not // return anything - $backend->expects($this->at(1)) + $backend->expects($this->once()) ->method('getHome') ->with($this->equalTo('foo')) ->willReturn('/home/foo'); - $backend->expects($this->at(2)) + $backend->expects($this->once()) ->method('deleteUser') ->with($this->equalTo('foo')); @@ -257,9 +258,9 @@ class UserTest extends TestCase { $this->assertTrue($user->delete()); } - public function testGetHome() { + public function testGetHome(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -281,16 +282,16 @@ class UserTest extends TestCase { $this->assertEquals('/home/foo', $user->getHome()); } - public function testGetBackendClassName() { + public function testGetBackendClassName(): void { $user = new User('foo', new \Test\Util\User\Dummy(), $this->dispatcher); $this->assertEquals('Dummy', $user->getBackendClassName()); - $user = new User('foo', new \OC\User\Database(), $this->dispatcher); + $user = new User('foo', new Database(), $this->dispatcher); $this->assertEquals('Database', $user->getBackendClassName()); } - public function testGetHomeNotSupported() { + public function testGetHomeNotSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->never()) @@ -307,7 +308,7 @@ class UserTest extends TestCase { ->method('getUserValue') ->willReturn(true); $allConfig->expects($this->any()) - ->method('getSystemValue') + ->method('getSystemValueString') ->with($this->equalTo('datadirectory')) ->willReturn('arbitrary/path'); @@ -315,9 +316,9 @@ class UserTest extends TestCase { $this->assertEquals('arbitrary/path/foo', $user->getHome()); } - public function testCanChangePassword() { + public function testCanChangePassword(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -335,9 +336,9 @@ class UserTest extends TestCase { $this->assertTrue($user->canChangePassword()); } - public function testCanChangePasswordNotSupported() { + public function testCanChangePasswordNotSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -349,9 +350,9 @@ class UserTest extends TestCase { $this->assertFalse($user->canChangePassword()); } - public function testCanChangeDisplayName() { + public function testCanChangeDisplayName(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -365,13 +366,18 @@ class UserTest extends TestCase { } }); - $user = new User('foo', $backend, $this->dispatcher); + $config = $this->createMock(IConfig::class); + $config->method('getSystemValueBool') + ->with('allow_user_to_change_display_name') + ->willReturn(true); + + $user = new User('foo', $backend, $this->dispatcher, null, $config); $this->assertTrue($user->canChangeDisplayName()); } - public function testCanChangeDisplayNameNotSupported() { + public function testCanChangeDisplayNameNotSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -383,11 +389,11 @@ class UserTest extends TestCase { $this->assertFalse($user->canChangeDisplayName()); } - public function testSetDisplayNameSupported() { + public function testSetDisplayNameSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ - $backend = $this->createMock(\OC\User\Database::class); + $backend = $this->createMock(Database::class); $backend->expects($this->any()) ->method('implementsActions') @@ -404,7 +410,7 @@ class UserTest extends TestCase { ->with('foo', 'Foo') ->willReturn(true); - $user = new User('foo', $backend, $this->dispatcher); + $user = new User('foo', $backend, $this->createMock(IEventDispatcher::class)); $this->assertTrue($user->setDisplayName('Foo')); $this->assertEquals('Foo', $user->getDisplayName()); } @@ -412,11 +418,11 @@ class UserTest extends TestCase { /** * don't allow display names containing whitespaces only */ - public function testSetDisplayNameEmpty() { + public function testSetDisplayNameEmpty(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ - $backend = $this->createMock(\OC\User\Database::class); + $backend = $this->createMock(Database::class); $backend->expects($this->any()) ->method('implementsActions') @@ -433,11 +439,11 @@ class UserTest extends TestCase { $this->assertEquals('foo', $user->getDisplayName()); } - public function testSetDisplayNameNotSupported() { + public function testSetDisplayNameNotSupported(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ - $backend = $this->createMock(\OC\User\Database::class); + $backend = $this->createMock(Database::class); $backend->expects($this->any()) ->method('implementsActions') @@ -451,12 +457,12 @@ class UserTest extends TestCase { $this->assertEquals('foo', $user->getDisplayName()); } - public function testSetPasswordHooks() { + public function testSetPasswordHooks(): void { $hooksCalled = 0; $test = $this; /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) @@ -466,7 +472,7 @@ class UserTest extends TestCase { * @param User $user * @param string $password */ - $hook = function ($user, $password) use ($test, &$hooksCalled) { + $hook = function ($user, $password) use ($test, &$hooksCalled): void { $hooksCalled++; $test->assertEquals('foo', $user->getUID()); $test->assertEquals('bar', $password); @@ -492,7 +498,7 @@ class UserTest extends TestCase { $this->assertEquals(2, $hooksCalled); } - public function dataDeleteHooks() { + public static function dataDeleteHooks(): array { return [ [true, 2], [false, 1], @@ -500,28 +506,39 @@ class UserTest extends TestCase { } /** - * @dataProvider dataDeleteHooks * @param bool $result * @param int $expectedHooks */ - public function testDeleteHooks($result, $expectedHooks) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataDeleteHooks')] + public function testDeleteHooks($result, $expectedHooks): void { $hooksCalled = 0; $test = $this; /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface&MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('deleteUser') ->willReturn($result); + + $config = $this->createMock(IConfig::class); + $config->method('getSystemValue') + ->willReturnArgument(1); + $config->method('getSystemValueString') + ->willReturnArgument(1); + $config->method('getSystemValueBool') + ->willReturnArgument(1); + $config->method('getSystemValueInt') + ->willReturnArgument(1); + $emitter = new PublicEmitter(); - $user = new User('foo', $backend, $this->dispatcher, $emitter); + $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); /** * @param User $user */ - $hook = function ($user) use ($test, &$hooksCalled) { + $hook = function ($user) use ($test, &$hooksCalled): void { $hooksCalled++; $test->assertEquals('foo', $user->getUID()); }; @@ -529,15 +546,11 @@ class UserTest extends TestCase { $emitter->listen('\OC\User', 'preDelete', $hook); $emitter->listen('\OC\User', 'postDelete', $hook); - $config = $this->createMock(IConfig::class); $commentsManager = $this->createMock(ICommentsManager::class); $notificationManager = $this->createMock(INotificationManager::class); - $config->method('getSystemValue') - ->willReturnArgument(1); - if ($result) { - $config->expects($this->once()) + $config->expects($this->atLeastOnce()) ->method('deleteAllUserValues') ->with('foo'); @@ -574,39 +587,93 @@ class UserTest extends TestCase { ->method('markProcessed'); } - $this->overwriteService(\OCP\Notification\IManager::class, $notificationManager); - $this->overwriteService(\OCP\Comments\ICommentsManager::class, $commentsManager); - $this->overwriteService(AllConfig::class, $config); + $this->overwriteService(INotificationManager::class, $notificationManager); + $this->overwriteService(ICommentsManager::class, $commentsManager); $this->assertSame($result, $user->delete()); $this->restoreService(AllConfig::class); - $this->restoreService(\OCP\Comments\ICommentsManager::class); - $this->restoreService(\OCP\Notification\IManager::class); + $this->restoreService(ICommentsManager::class); + $this->restoreService(INotificationManager::class); $this->assertEquals($expectedHooks, $hooksCalled); } - public function testGetCloudId() { - /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend - */ + public function testDeleteRecoverState() { $backend = $this->createMock(\Test\Util\User\Dummy::class); - $urlGenerator = $this->getMockBuilder('\OC\URLGenerator') - ->setMethods(['getAbsoluteURL']) - ->disableOriginalConstructor()->getMock(); - $urlGenerator - ->expects($this->any()) - ->method('getAbsoluteURL') - ->withAnyParameters() - ->willReturn('http://localhost:8888/owncloud'); + $backend->expects($this->once()) + ->method('deleteUser') + ->willReturn(true); + + $config = $this->createMock(IConfig::class); + $config->method('getSystemValue') + ->willReturnArgument(1); + $config->method('getSystemValueString') + ->willReturnArgument(1); + $config->method('getSystemValueBool') + ->willReturnArgument(1); + $config->method('getSystemValueInt') + ->willReturnArgument(1); + + $userConfig = []; + $config->expects(self::atLeast(2)) + ->method('setUserValue') + ->willReturnCallback(function (): void { + $userConfig[] = func_get_args(); + }); + + $commentsManager = $this->createMock(ICommentsManager::class); + $commentsManager->expects($this->once()) + ->method('deleteReferencesOfActor') + ->willThrowException(new \Error('Test exception')); + + $this->overwriteService(ICommentsManager::class, $commentsManager); + $this->expectException(\Error::class); + + $user = $this->getMockBuilder(User::class) + ->onlyMethods(['getHome']) + ->setConstructorArgs(['foo', $backend, $this->dispatcher, null, $config]) + ->getMock(); + + $user->expects(self::atLeastOnce()) + ->method('getHome') + ->willReturn('/home/path'); + + $user->delete(); + + $this->assertEqualsCanonicalizing( + [ + ['foo', 'core', 'deleted', 'true', null], + ['foo', 'core', 'deleted.backup-home', '/home/path', null], + ], + $userConfig, + ); + + $this->restoreService(ICommentsManager::class); + } + + public static function dataGetCloudId(): array { + return [ + ['https://localhost:8888/nextcloud', 'foo@localhost:8888/nextcloud'], + ['http://localhost:8888/nextcloud', 'foo@http://localhost:8888/nextcloud'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetCloudId')] + public function testGetCloudId(string $absoluteUrl, string $cloudId): void { + /** @var Backend|MockObject $backend */ + $backend = $this->createMock(\Test\Util\User\Dummy::class); + $urlGenerator = $this->createMock(IURLGenerator::class); + $urlGenerator->method('getAbsoluteURL') + ->withAnyParameters() + ->willReturn($absoluteUrl); $user = new User('foo', $backend, $this->dispatcher, null, null, $urlGenerator); - $this->assertEquals('foo@localhost:8888/owncloud', $user->getCloudId()); + $this->assertEquals($cloudId, $user->getCloudId()); } - public function testSetEMailAddressEmpty() { + public function testSetEMailAddressEmpty(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -618,7 +685,7 @@ class UserTest extends TestCase { * @param string $feature * @param string $value */ - $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled) { + $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled): void { $hooksCalled++; $test->assertEquals('eMailAddress', $feature); $test->assertEquals('', $value); @@ -640,9 +707,9 @@ class UserTest extends TestCase { $user->setEMailAddress(''); } - public function testSetEMailAddress() { + public function testSetEMailAddress(): void { /** - * @var UserInterface | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -654,7 +721,7 @@ class UserTest extends TestCase { * @param string $feature * @param string $value */ - $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled) { + $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled): void { $hooksCalled++; $test->assertEquals('eMailAddress', $feature); $test->assertEquals('foo@bar.com', $value); @@ -677,18 +744,19 @@ class UserTest extends TestCase { $user->setEMailAddress('foo@bar.com'); } - public function testSetEMailAddressNoChange() { + public function testSetEMailAddressNoChange(): void { /** - * @var UserInterface | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); - /** @var PublicEmitter|\PHPUnit\Framework\MockObject\MockObject $emitter */ + /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); - $this->dispatcher->expects($this->never()) + $dispatcher = $this->createMock(IEventDispatcher::class); + $dispatcher->expects($this->never()) ->method('dispatch'); $config = $this->createMock(IConfig::class); @@ -698,13 +766,13 @@ class UserTest extends TestCase { $config->expects($this->any()) ->method('setUserValue'); - $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); + $user = new User('foo', $backend, $dispatcher, $emitter, $config); $user->setEMailAddress('foo@bar.com'); } - public function testSetQuota() { + public function testSetQuota(): void { /** - * @var UserInterface | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -716,7 +784,7 @@ class UserTest extends TestCase { * @param string $feature * @param string $value */ - $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled) { + $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled): void { $hooksCalled++; $test->assertEquals('quota', $feature); $test->assertEquals('23 TB', $value); @@ -739,13 +807,13 @@ class UserTest extends TestCase { $user->setQuota('23 TB'); } - public function testGetDefaultUnlimitedQuota() { + public function testGetDefaultUnlimitedQuota(): void { /** - * @var UserInterface | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); - /** @var PublicEmitter|\PHPUnit\Framework\MockObject\MockObject $emitter */ + /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); @@ -762,21 +830,21 @@ class UserTest extends TestCase { ['files', 'allow_unlimited_quota', '1', '1'], ]; $config->method('getUserValue') - ->will($this->returnValueMap($userValueMap)); + ->willReturnMap($userValueMap); $config->method('getAppValue') - ->will($this->returnValueMap($appValueMap)); + ->willReturnMap($appValueMap); - $quota = $user->getQuota(); - $this->assertEquals('none', $quota); + $this->assertEquals('none', $user->getQuota()); + $this->assertEquals(FileInfo::SPACE_UNLIMITED, $user->getQuotaBytes()); } - public function testGetDefaultUnlimitedQuotaForbidden() { + public function testGetDefaultUnlimitedQuotaForbidden(): void { /** - * @var UserInterface | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); - /** @var PublicEmitter|\PHPUnit\Framework\MockObject\MockObject $emitter */ + /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); @@ -796,21 +864,21 @@ class UserTest extends TestCase { ['files', 'default_quota', '1 GB', '1 GB'], ]; $config->method('getUserValue') - ->will($this->returnValueMap($userValueMap)); + ->willReturnMap($userValueMap); $config->method('getAppValue') - ->will($this->returnValueMap($appValueMap)); + ->willReturnMap($appValueMap); - $quota = $user->getQuota(); - $this->assertEquals('1 GB', $quota); + $this->assertEquals('1 GB', $user->getQuota()); + $this->assertEquals(1024 * 1024 * 1024, $user->getQuotaBytes()); } - public function testSetQuotaAddressNoChange() { + public function testSetQuotaAddressNoChange(): void { /** - * @var UserInterface | \PHPUnit\Framework\MockObject\MockObject $backend + * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); - /** @var PublicEmitter|\PHPUnit\Framework\MockObject\MockObject $emitter */ + /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); @@ -826,9 +894,9 @@ class UserTest extends TestCase { $user->setQuota('23 TB'); } - public function testGetLastLogin() { + public function testGetLastLogin(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -846,9 +914,9 @@ class UserTest extends TestCase { $this->assertSame(42, $user->getLastLogin()); } - public function testSetEnabled() { + public function testSetEnabled(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -861,14 +929,20 @@ class UserTest extends TestCase { $this->equalTo('enabled'), 'true' ); + /* dav event listener gets the manager list from config */ + $config->expects(self::any()) + ->method('getUserValue') + ->willReturnCallback( + fn ($user, $app, $key, $default) => ($key === 'enabled' ? 'false' : $default) + ); $user = new User('foo', $backend, $this->dispatcher, null, $config); $user->setEnabled(true); } - public function testSetDisabled() { + public function testSetDisabled(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -890,7 +964,7 @@ class UserTest extends TestCase { null, $config, ]) - ->setMethods(['isEnabled', 'triggerChange']) + ->onlyMethods(['isEnabled', 'triggerChange']) ->getMock(); $user->expects($this->once()) @@ -906,9 +980,9 @@ class UserTest extends TestCase { $user->setEnabled(false); } - public function testSetDisabledAlreadyDisabled() { + public function testSetDisabledAlreadyDisabled(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); @@ -924,7 +998,7 @@ class UserTest extends TestCase { null, $config, ]) - ->setMethods(['isEnabled', 'triggerChange']) + ->onlyMethods(['isEnabled', 'triggerChange']) ->getMock(); $user->expects($this->once()) @@ -936,9 +1010,9 @@ class UserTest extends TestCase { $user->setEnabled(false); } - public function testGetEMailAddress() { + public function testGetEMailAddress(): void { /** - * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend + * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); |