diff options
Diffstat (limited to 'apps/twofactor_backupcodes/tests/Unit')
14 files changed, 1418 insertions, 0 deletions
diff --git a/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php b/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php new file mode 100644 index 00000000000..152ff80194a --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php @@ -0,0 +1,106 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Test\Unit\Activity; + +use OCA\TwoFactorBackupCodes\Activity\Provider; +use OCP\Activity\Exceptions\UnknownActivityException; +use OCP\Activity\IEvent; +use OCP\Activity\IManager; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\L10N\IFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class ProviderTest extends TestCase { + private IFactory&MockObject $l10n; + private IURLGenerator&MockObject $urlGenerator; + private IManager&MockObject $activityManager; + private Provider $provider; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IFactory::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->activityManager = $this->createMock(IManager::class); + + $this->provider = new Provider($this->l10n, $this->urlGenerator, $this->activityManager); + } + + public function testParseUnrelated(): void { + $lang = 'ru'; + $event = $this->createMock(IEvent::class); + $event->expects($this->once()) + ->method('getApp') + ->willReturn('comments'); + $this->expectException(UnknownActivityException::class); + + $this->provider->parse($lang, $event); + } + + public static function subjectData(): array { + return [ + ['codes_generated'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('subjectData')] + public function testParse(string $subject): void { + $lang = 'ru'; + $event = $this->createMock(IEvent::class); + $l = $this->createMock(IL10N::class); + + $event->expects($this->once()) + ->method('getApp') + ->willReturn('twofactor_backupcodes'); + $this->l10n->expects($this->once()) + ->method('get') + ->with('twofactor_backupcodes', $lang) + ->willReturn($l); + $this->urlGenerator->expects($this->once()) + ->method('imagePath') + ->with('core', 'actions/password.svg') + ->willReturn('path/to/image'); + $this->urlGenerator->expects($this->once()) + ->method('getAbsoluteURL') + ->with('path/to/image') + ->willReturn('absolute/path/to/image'); + $event->expects($this->once()) + ->method('setIcon') + ->with('absolute/path/to/image'); + $event->expects($this->once()) + ->method('getSubject') + ->willReturn($subject); + $event->expects($this->once()) + ->method('setParsedSubject'); + + $this->provider->parse($lang, $event); + } + + public function testParseInvalidSubject(): void { + $lang = 'ru'; + $l = $this->createMock(IL10N::class); + $event = $this->createMock(IEvent::class); + + $event->expects($this->once()) + ->method('getApp') + ->willReturn('twofactor_backupcodes'); + $this->l10n->expects($this->once()) + ->method('get') + ->with('twofactor_backupcodes', $lang) + ->willReturn($l); + $event->expects($this->once()) + ->method('getSubject') + ->willReturn('unrelated'); + + $this->expectException(UnknownActivityException::class); + $this->provider->parse($lang, $event); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/CheckBackupCodeTest.php b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/CheckBackupCodeTest.php new file mode 100644 index 00000000000..87bc65e4309 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/CheckBackupCodeTest.php @@ -0,0 +1,129 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\BackgroundJob; + +use OC\Authentication\TwoFactorAuth\Manager; +use OCA\TwoFactorBackupCodes\BackgroundJob\CheckBackupCodes; +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\BackgroundJob\IJobList; +use OCP\IUser; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class CheckBackupCodeTest extends TestCase { + private IUserManager&MockObject $userManager; + private IJobList&MockObject $jobList; + private IRegistry&MockObject $registry; + private Manager&MockObject $manager; + private IUser&MockObject $user; + private CheckBackupCodes $checkBackupCodes; + + protected function setUp(): void { + parent::setUp(); + + $this->userManager = $this->createMock(IUserManager::class); + $this->jobList = $this->createMock(IJobList::class); + $this->registry = $this->createMock(IRegistry::class); + $this->manager = $this->createMock(Manager::class); + + $this->user = $this->createMock(IUser::class); + + $this->userManager->method('callForSeenUsers') + ->willReturnCallback(function (\Closure $e): void { + $e($this->user); + }); + + $this->checkBackupCodes = new CheckBackupCodes( + $this->createMock(ITimeFactory::class), + $this->userManager, + $this->jobList, + $this->manager, + $this->registry + ); + } + + public function testRunAlreadyGenerated(): void { + $this->user->method('isEnabled') + ->willReturn(true); + + $this->registry->method('getProviderStates') + ->with($this->user) + ->willReturn(['backup_codes' => true]); + $this->manager->method('isTwoFactorAuthenticated') + ->with($this->user) + ->willReturn(true); + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->invokePrivate($this->checkBackupCodes, 'run', [[]]); + } + + public function testRun(): void { + $this->user->method('getUID') + ->willReturn('myUID'); + $this->user->method('isEnabled') + ->willReturn(true); + + $this->registry->expects($this->once()) + ->method('getProviderStates') + ->with($this->user) + ->willReturn([ + 'backup_codes' => false, + ]); + $this->jobList->expects($this->once()) + ->method('add') + ->with( + $this->equalTo(RememberBackupCodesJob::class), + ['uid' => 'myUID'] + ); + $this->manager->method('isTwoFactorAuthenticated') + ->with($this->user) + ->willReturn(true); + + $this->invokePrivate($this->checkBackupCodes, 'run', [[]]); + } + + public function testRunDisabledUser(): void { + $this->user->method('getUID') + ->willReturn('myUID'); + $this->user->method('isEnabled') + ->willReturn(false); + + $this->registry->expects($this->never()) + ->method('getProviderStates') + ->with($this->user); + + $this->jobList->expects($this->never()) + ->method('add'); + + $this->invokePrivate($this->checkBackupCodes, 'run', [[]]); + } + + public function testRunNoProviders(): void { + $this->user->method('isEnabled') + ->willReturn(true); + + $this->registry->expects($this->once()) + ->method('getProviderStates') + ->with($this->user) + ->willReturn([ + 'backup_codes' => false, + ]); + $this->jobList->expects($this->never()) + ->method($this->anything()); + $this->manager->method('isTwoFactorAuthenticated') + ->with($this->user) + ->willReturn(false); + + $this->invokePrivate($this->checkBackupCodes, 'run', [[]]); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php new file mode 100644 index 00000000000..6b162894258 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php @@ -0,0 +1,213 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\BackgroundJob; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\BackgroundJob\IJobList; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Notification\IManager; +use OCP\Notification\INotification; +use OCP\Server; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class RememberBackupCodesJobTest extends TestCase { + private IRegistry&MockObject $registry; + private IUserManager&MockObject $userManager; + private ITimeFactory&MockObject $time; + private IManager&MockObject $notificationManager; + private IJobList&MockObject $jobList; + private RememberBackupCodesJob $job; + + protected function setUp(): void { + parent::setUp(); + + $this->registry = $this->createMock(IRegistry::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->time = $this->createMock(ITimeFactory::class); + $this->time->method('getTime') + ->willReturn(10000000); + $this->notificationManager = $this->createMock(IManager::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->job = new RememberBackupCodesJob( + $this->registry, + $this->userManager, + $this->time, + $this->notificationManager, + $this->jobList + ); + } + + public function testInvalidUID(): void { + $this->userManager->method('get') + ->with('invalidUID') + ->willReturn(null); + + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + RememberBackupCodesJob::class, + ['uid' => 'invalidUID'] + ); + $this->jobList->expects($this->never()) + ->method('add'); + + self::invokePrivate($this->job, 'run', [['uid' => 'invalidUID']]); + } + + public function testBackupCodesGenerated(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('validUID'); + $user->method('isEnabled') + ->willReturn(true); + + $this->userManager->method('get') + ->with('validUID') + ->willReturn($user); + + $this->registry->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => true + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + RememberBackupCodesJob::class, + ['uid' => 'validUID'] + ); + + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + + self::invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } + + public function testNoActiveProvider(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('validUID'); + $this->userManager->method('get') + ->with('validUID') + ->willReturn($user); + + $this->registry->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => false, + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + RememberBackupCodesJob::class, + ['uid' => 'validUID'] + ); + + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + + self::invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } + + public function testNotificationSend(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('validUID'); + $user->method('isEnabled') + ->willReturn(true); + $this->userManager->method('get') + ->with('validUID') + ->willReturn($user); + + $this->registry->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => true, + ]); + + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $date = new \DateTime(); + $date->setTimestamp($this->time->getTime()); + + $this->notificationManager->method('createNotification') + ->willReturn(Server::get(IManager::class)->createNotification()); + + $this->notificationManager->expects($this->once()) + ->method('notify') + ->with($this->callback(function (INotification $n) { + return $n->getApp() === 'twofactor_backupcodes' + && $n->getUser() === 'validUID' + && $n->getDateTime()->getTimestamp() === 10000000 + && $n->getObjectType() === 'create' + && $n->getObjectId() === 'codes' + && $n->getSubject() === 'create_backupcodes'; + })); + + self::invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } + + public function testNotificationNotSendForDisabledUser(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('validUID'); + $user->method('isEnabled') + ->willReturn(false); + $this->userManager->method('get') + ->with('validUID') + ->willReturn($user); + + $this->registry->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => true, + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + RememberBackupCodesJob::class, + ['uid' => 'validUID'] + ); + + $date = new \DateTime(); + $date->setTimestamp($this->time->getTime()); + + $this->notificationManager->method('createNotification') + ->willReturn(Server::get(IManager::class)->createNotification()); + + $this->notificationManager->expects($this->once()) + ->method('markProcessed') + ->with($this->callback(function (INotification $n) { + return $n->getApp() === 'twofactor_backupcodes' + && $n->getUser() === 'validUID' + && $n->getObjectType() === 'create' + && $n->getObjectId() === 'codes' + && $n->getSubject() === 'create_backupcodes'; + })); + + $this->notificationManager->expects($this->never()) + ->method('notify'); + + self::invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php b/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php new file mode 100644 index 00000000000..02c42b65148 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Controller/SettingsControllerTest.php @@ -0,0 +1,60 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Controller; + +use OCA\TwoFactorBackupCodes\Controller\SettingsController; +use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IRequest; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class SettingsControllerTest extends TestCase { + private IRequest&MockObject $request; + private BackupCodeStorage&MockObject $storage; + private IUserSession&MockObject $userSession; + private SettingsController $controller; + + protected function setUp(): void { + parent::setUp(); + + $this->request = $this->createMock(IRequest::class); + $this->storage = $this->createMock(BackupCodeStorage::class); + $this->userSession = $this->createMock(IUserSession::class); + + $this->controller = new SettingsController('twofactor_backupcodes', $this->request, $this->storage, $this->userSession); + } + + public function testCreateCodes(): void { + $user = $this->createMock(IUser::class); + + $codes = ['a', 'b']; + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->storage->expects($this->once()) + ->method('createCodes') + ->with($user) + ->willReturn($codes); + $this->storage->expects($this->once()) + ->method('getBackupCodesState') + ->with($user) + ->willReturn(['state']); + + $expected = [ + 'codes' => $codes, + 'state' => ['state'], + ]; + $response = $this->controller->createCodes(); + $this->assertInstanceOf(JSONResponse::class, $response); + $this->assertEquals($expected, $response->getData()); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Event/CodesGeneratedTest.php b/apps/twofactor_backupcodes/tests/Unit/Event/CodesGeneratedTest.php new file mode 100644 index 00000000000..3f619e387e9 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Event/CodesGeneratedTest.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Event; + +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCP\IUser; +use Test\TestCase; + +class CodesGeneratedTest extends TestCase { + public function testCodeGeneratedEvent(): void { + $user = $this->createMock(IUser::class); + + $event = new CodesGenerated($user); + + $this->assertSame($user, $event->getUser()); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Listener/ActivityPublisherTest.php b/apps/twofactor_backupcodes/tests/Unit/Listener/ActivityPublisherTest.php new file mode 100644 index 00000000000..bd944dc2396 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/ActivityPublisherTest.php @@ -0,0 +1,72 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Listener; + +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCA\TwoFactorBackupCodes\Listener\ActivityPublisher; +use OCP\Activity\IEvent; +use OCP\Activity\IManager; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class ActivityPublisherTest extends TestCase { + private IManager&MockObject $activityManager; + private LoggerInterface&MockObject $logger; + private ActivityPublisher $listener; + + protected function setUp(): void { + parent::setUp(); + + $this->activityManager = $this->createMock(IManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->listener = new ActivityPublisher($this->activityManager, $this->logger); + } + + public function testHandleGenericEvent(): void { + $event = $this->createMock(Event::class); + $this->activityManager->expects($this->never()) + ->method('publish'); + + $this->listener->handle($event); + } + + public function testHandleCodesGeneratedEvent(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('fritz'); + $event = new CodesGenerated($user); + $activityEvent = $this->createMock(IEvent::class); + $this->activityManager->expects($this->once()) + ->method('generateEvent') + ->willReturn($activityEvent); + $activityEvent->expects($this->once()) + ->method('setApp') + ->with('twofactor_backupcodes') + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setType') + ->with('security') + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setAuthor') + ->with('fritz') + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setAffectedUser') + ->with('fritz') + ->willReturnSelf(); + $this->activityManager->expects($this->once()) + ->method('publish'); + + $this->listener->handle($event); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Listener/ClearNotificationsTest.php b/apps/twofactor_backupcodes/tests/Unit/Listener/ClearNotificationsTest.php new file mode 100644 index 00000000000..229d8df05d3 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/ClearNotificationsTest.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Listener; + +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCA\TwoFactorBackupCodes\Listener\ClearNotifications; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use OCP\Notification\IManager; +use OCP\Notification\INotification; +use OCP\Server; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class ClearNotificationsTest extends TestCase { + private IManager&MockObject $notificationManager; + private ClearNotifications $listener; + + protected function setUp(): void { + parent::setUp(); + + $this->notificationManager = $this->createMock(IManager::class); + $this->notificationManager->method('createNotification') + ->willReturn(Server::get(IManager::class)->createNotification()); + + $this->listener = new ClearNotifications($this->notificationManager); + } + + public function testHandleGenericEvent(): void { + $event = $this->createMock(Event::class); + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleCodesGeneratedEvent(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('fritz'); + $event = new CodesGenerated($user); + + $this->notificationManager->expects($this->once()) + ->method('markProcessed') + ->with($this->callback(function (INotification $n) { + return $n->getUser() === 'fritz' + && $n->getApp() === 'twofactor_backupcodes' + && $n->getObjectType() === 'create' + && $n->getObjectId() === 'codes'; + })); + + $this->listener->handle($event); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderDisabledTest.php b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderDisabledTest.php new file mode 100644 index 00000000000..ea4f530cab4 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderDisabledTest.php @@ -0,0 +1,88 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCA\TwoFactorBackupCodes\Listener\ProviderDisabled; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserUnregistered; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class ProviderDisabledTest extends TestCase { + private IRegistry&MockObject $registy; + private IJobList&MockObject $jobList; + private ProviderDisabled $listener; + + protected function setUp(): void { + parent::setUp(); + + $this->registy = $this->createMock(IRegistry::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->listener = new ProviderDisabled($this->registy, $this->jobList); + } + + public function testHandleGenericEvent(): void { + $event = $this->createMock(Event::class); + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleStillActiveProvider(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(TwoFactorProviderForUserUnregistered::class); + $event->method('getUser') + ->willReturn($user); + + $this->registy->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => true, + ]); + + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleNoActiveProvider(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(TwoFactorProviderForUserUnregistered::class); + $event->method('getUser') + ->willReturn($user); + + $this->registy->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + 'foo' => false, + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + $this->equalTo(RememberBackupCodesJob::class), + $this->equalTo(['uid' => 'myUID']) + ); + + $this->listener->handle($event); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderEnabledTest.php b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderEnabledTest.php new file mode 100644 index 00000000000..50aac6139c0 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderEnabledTest.php @@ -0,0 +1,86 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCA\TwoFactorBackupCodes\Listener\ProviderEnabled; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class ProviderEnabledTest extends TestCase { + private IRegistry&MockObject $registy; + private IJobList&MockObject $jobList; + private ProviderEnabled $listener; + + protected function setUp(): void { + parent::setUp(); + + $this->registy = $this->createMock(IRegistry::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->listener = new ProviderEnabled($this->registy, $this->jobList); + } + + public function testHandleGenericEvent(): void { + $event = $this->createMock(Event::class); + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleCodesGeneratedEventAlraedyBackupcodes(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(TwoFactorProviderForUserRegistered::class); + $event->method('getUser') + ->willReturn($user); + + $this->registy->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => true, + ]); + + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleCodesGeneratedEventNoBackupcodes(): void { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(TwoFactorProviderForUserRegistered::class); + $event->method('getUser') + ->willReturn($user); + + $this->registy->method('getProviderStates') + ->with($user) + ->willReturn([ + 'backup_codes' => false, + ]); + + $this->jobList->expects($this->once()) + ->method('add') + ->with( + $this->equalTo(RememberBackupCodesJob::class), + $this->equalTo(['uid' => 'myUID']) + ); + + $this->listener->handle($event); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Listener/RegistryUpdaterTest.php b/apps/twofactor_backupcodes/tests/Unit/Listener/RegistryUpdaterTest.php new file mode 100644 index 00000000000..86d890f0d5e --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/RegistryUpdaterTest.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Listener; + +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCA\TwoFactorBackupCodes\Listener\RegistryUpdater; +use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\EventDispatcher\Event; +use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class RegistryUpdaterTest extends TestCase { + private IRegistry&MockObject $registry; + private BackupCodesProvider&MockObject $provider; + private RegistryUpdater $listener; + + protected function setUp(): void { + parent::setUp(); + + $this->registry = $this->createMock(IRegistry::class); + $this->provider = $this->createMock(BackupCodesProvider::class); + + $this->listener = new RegistryUpdater($this->registry, $this->provider); + } + + public function testHandleGenericEvent(): void { + $event = $this->createMock(Event::class); + $this->registry->expects($this->never()) + ->method('enableProviderFor'); + + $this->listener->handle($event); + } + + public function testHandleCodesGeneratedEvent(): void { + $user = $this->createMock(IUser::class); + $event = new CodesGenerated($user); + $this->registry->expects($this->once()) + ->method('enableProviderFor') + ->with( + $this->provider, + $user + ); + + $this->listener->handle($event); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Migration/CheckBackupCodeTest.php b/apps/twofactor_backupcodes/tests/Unit/Migration/CheckBackupCodeTest.php new file mode 100644 index 00000000000..c68ab185116 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Migration/CheckBackupCodeTest.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Migration; + +use OCA\TwoFactorBackupCodes\Migration\CheckBackupCodes; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use PHPunit\Framework\MockObject\MockObject; +use Test\TestCase; + +class CheckBackupCodeTest extends TestCase { + private IJobList&MockObject $jobList; + private CheckBackupCodes $checkBackupsCodes; + + protected function setUp(): void { + parent::setUp(); + + $this->jobList = $this->createMock(IJobList::class); + $this->checkBackupsCodes = new CheckBackupCodes($this->jobList); + } + + public function testGetName(): void { + $this->assertSame('Add background job to check for backup codes', $this->checkBackupsCodes->getName()); + } + + public function testRun(): void { + $this->jobList->expects($this->once()) + ->method('add') + ->with( + $this->equalTo(\OCA\TwoFactorBackupCodes\BackgroundJob\CheckBackupCodes::class) + ); + + $this->checkBackupsCodes->run($this->createMock(IOutput::class)); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Notification/NotifierTest.php b/apps/twofactor_backupcodes/tests/Unit/Notification/NotifierTest.php new file mode 100644 index 00000000000..b091d57dbd2 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Notification/NotifierTest.php @@ -0,0 +1,114 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Notification; + +use OCA\TwoFactorBackupCodes\Notifications\Notifier; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\L10N\IFactory; +use OCP\Notification\INotification; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class NotifierTest extends TestCase { + protected IFactory&MockObject $factory; + protected IURLGenerator&MockObject $url; + protected IL10N&MockObject $l; + protected Notifier $notifier; + + protected function setUp(): void { + parent::setUp(); + + $this->l = $this->createMock(IL10N::class); + $this->l->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($string, $args) { + return vsprintf($string, $args); + }); + $this->factory = $this->createMock(IFactory::class); + $this->url = $this->createMock(IURLGenerator::class); + $this->factory->expects($this->any()) + ->method('get') + ->willReturn($this->l); + + $this->notifier = new Notifier( + $this->factory, + $this->url + ); + } + + + public function testPrepareWrongApp(): void { + $this->expectException(\InvalidArgumentException::class); + + /** @var INotification|MockObject $notification */ + $notification = $this->createMock(INotification::class); + $notification->expects($this->once()) + ->method('getApp') + ->willReturn('notifications'); + $notification->expects($this->never()) + ->method('getSubject'); + + $this->notifier->prepare($notification, 'en'); + } + + + public function testPrepareWrongSubject(): void { + $this->expectException(\InvalidArgumentException::class); + + /** @var INotification|MockObject $notification */ + $notification = $this->createMock(INotification::class); + $notification->expects($this->once()) + ->method('getApp') + ->willReturn('twofactor_backupcodes'); + $notification->expects($this->once()) + ->method('getSubject') + ->willReturn('wrong subject'); + + $this->notifier->prepare($notification, 'en'); + } + + public function testPrepare(): void { + /** @var INotification&MockObject $notification */ + $notification = $this->createMock(INotification::class); + + $notification->expects($this->once()) + ->method('getApp') + ->willReturn('twofactor_backupcodes'); + $notification->expects($this->once()) + ->method('getSubject') + ->willReturn('create_backupcodes'); + + $this->factory->expects($this->once()) + ->method('get') + ->with('twofactor_backupcodes', 'nl') + ->willReturn($this->l); + + $notification->expects($this->once()) + ->method('setParsedSubject') + ->with('Generate backup codes') + ->willReturnSelf(); + $notification->expects($this->once()) + ->method('setParsedMessage') + ->with('You enabled two-factor authentication but did not generate backup codes yet. They are needed to restore access to your account in case you lose your second factor.') + ->willReturnSelf(); + + $this->url->expects($this->once()) + ->method('linkToRouteAbsolute') + ->with('settings.PersonalSettings.index', ['section' => 'security']) + ->willReturn('linkToRouteAbsolute'); + $notification->expects($this->once()) + ->method('setLink') + ->with('linkToRouteAbsolute') + ->willReturnSelf(); + + $return = $this->notifier->prepare($notification, 'nl'); + $this->assertEquals($notification, $return); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Provider/BackupCodesProviderTest.php b/apps/twofactor_backupcodes/tests/Unit/Provider/BackupCodesProviderTest.php new file mode 100644 index 00000000000..512374fca8c --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Provider/BackupCodesProviderTest.php @@ -0,0 +1,153 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Provider; + +use OC\App\AppManager; +use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; +use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; +use OCP\AppFramework\Services\IInitialState; +use OCP\IL10N; +use OCP\IUser; +use OCP\Server; +use OCP\Template\ITemplateManager; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class BackupCodesProviderTest extends TestCase { + private string $appName; + + private BackupCodeStorage&MockObject $storage; + private IL10N&MockObject $l10n; + private AppManager&MockObject $appManager; + private IInitialState&MockObject $initialState; + + private ITemplateManager $templateManager; + private BackupCodesProvider $provider; + + protected function setUp(): void { + parent::setUp(); + + $this->appName = 'twofactor_backupcodes'; + $this->storage = $this->createMock(BackupCodeStorage::class); + $this->l10n = $this->createMock(IL10N::class); + $this->appManager = $this->createMock(AppManager::class); + $this->initialState = $this->createMock(IInitialState::class); + $this->templateManager = Server::get(ITemplateManager::class); + + $this->provider = new BackupCodesProvider( + $this->appName, + $this->storage, + $this->l10n, + $this->appManager, + $this->initialState, + $this->templateManager, + ); + } + + public function testGetId(): void { + $this->assertEquals('backup_codes', $this->provider->getId()); + } + + public function testGetDisplayName(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Backup code') + ->willReturn('l10n backup code'); + $this->assertSame('l10n backup code', $this->provider->getDisplayName()); + } + + public function testGetDescription(): void { + $this->l10n->expects($this->once()) + ->method('t') + ->with('Use backup code') + ->willReturn('l10n use backup code'); + $this->assertSame('l10n use backup code', $this->provider->getDescription()); + } + + public function testGetTempalte(): void { + $user = $this->getMockBuilder(IUser::class)->getMock(); + $expected = $this->templateManager->getTemplate('twofactor_backupcodes', 'challenge'); + + $this->assertEquals($expected, $this->provider->getTemplate($user)); + } + + public function testVerfiyChallenge(): void { + $user = $this->getMockBuilder(IUser::class)->getMock(); + $challenge = 'xyz'; + + $this->storage->expects($this->once()) + ->method('validateCode') + ->with($user, $challenge) + ->willReturn(false); + + $this->assertFalse($this->provider->verifyChallenge($user, $challenge)); + } + + public function testIsTwoFactorEnabledForUser(): void { + $user = $this->getMockBuilder(IUser::class)->getMock(); + + $this->storage->expects($this->once()) + ->method('hasBackupCodes') + ->with($user) + ->willReturn(true); + + $this->assertTrue($this->provider->isTwoFactorAuthEnabledForUser($user)); + } + + public function testIsActiveNoProviders(): void { + $user = $this->getMockBuilder(IUser::class)->getMock(); + + $this->appManager->expects($this->once()) + ->method('getEnabledAppsForUser') + ->with($user) + ->willReturn([ + 'twofactor_backupcodes', + 'mail', + ]); + $this->appManager->expects($this->once()) + ->method('getAppInfo') + ->with('mail') + ->willReturn([ + 'two-factor-providers' => [], + ]); + + $this->assertFalse($this->provider->isActive($user)); + } + + public function testIsActiveWithProviders(): void { + $user = $this->getMockBuilder(IUser::class)->getMock(); + + $this->appManager->expects($this->once()) + ->method('getEnabledAppsForUser') + ->with($user) + ->willReturn([ + 'twofactor_backupcodes', + 'twofactor_u2f', + ]); + $this->appManager->expects($this->once()) + ->method('getAppInfo') + ->with('twofactor_u2f') + ->willReturn([ + 'two-factor-providers' => [ + 'OCA\TwoFactorU2F\Provider\U2FProvider', + ], + ]); + + $this->assertTrue($this->provider->isActive($user)); + } + + public function testDisable(): void { + $user = $this->getMockBuilder(IUser::class)->getMock(); + $this->storage->expects(self::once()) + ->method('deleteCodes') + ->with($user); + + $this->provider->disableFor($user); + } +} diff --git a/apps/twofactor_backupcodes/tests/Unit/Service/BackupCodeStorageTest.php b/apps/twofactor_backupcodes/tests/Unit/Service/BackupCodeStorageTest.php new file mode 100644 index 00000000000..069e50b71fd --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Service/BackupCodeStorageTest.php @@ -0,0 +1,220 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Service; + +use OCA\TwoFactorBackupCodes\Db\BackupCode; +use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper; +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IUser; +use OCP\Security\IHasher; +use OCP\Security\ISecureRandom; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class BackupCodeStorageTest extends TestCase { + private BackupCodeMapper&MockObject $mapper; + private ISecureRandom&MockObject $random; + private IHasher&MockObject $hasher; + private IEventDispatcher&MockObject $eventDispatcher; + private BackupCodeStorage $storage; + + protected function setUp(): void { + parent::setUp(); + + $this->mapper = $this->createMock(BackupCodeMapper::class); + $this->random = $this->createMock(ISecureRandom::class); + $this->hasher = $this->createMock(IHasher::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + + $this->storage = new BackupCodeStorage($this->mapper, $this->random, $this->hasher, $this->eventDispatcher); + } + + public function testCreateCodes(): void { + $user = $this->createMock(IUser::class); + $number = 5; + $user->method('getUID')->willReturn('fritz'); + $this->random->expects($this->exactly($number)) + ->method('generate') + ->with(16, ISecureRandom::CHAR_HUMAN_READABLE) + ->willReturn('CODEABCDEF'); + $this->hasher->expects($this->exactly($number)) + ->method('hash') + ->with('CODEABCDEF') + ->willReturn('HASHEDCODE'); + $row = new BackupCode(); + $row->setUserId('fritz'); + $row->setCode('HASHEDCODE'); + $row->setUsed(0); + $this->mapper->expects($this->exactly($number)) + ->method('insert') + ->with($this->equalTo($row)); + $this->eventDispatcher->expects($this->once()) + ->method('dispatchTyped') + ->with( + $this->equalTo(new CodesGenerated($user)) + ); + + $codes = $this->storage->createCodes($user, $number); + $this->assertCount($number, $codes); + foreach ($codes as $code) { + $this->assertEquals('CODEABCDEF', $code); + } + } + + public function testHasBackupCodes(): void { + $user = $this->createMock(IUser::class); + $codes = [ + new BackupCode(), + new BackupCode(), + ]; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + + $this->assertTrue($this->storage->hasBackupCodes($user)); + } + + public function testHasBackupCodesNoCodes(): void { + $user = $this->createMock(IUser::class); + $codes = []; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + + $this->assertFalse($this->storage->hasBackupCodes($user)); + } + + public function testGetBackupCodeState(): void { + $user = $this->createMock(IUser::class); + + $code1 = new BackupCode(); + $code1->setUsed(1); + $code2 = new BackupCode(); + $code2->setUsed('0'); + $codes = [ + $code1, + $code2, + ]; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + + $expected = [ + 'enabled' => true, + 'total' => 2, + 'used' => 1, + ]; + $this->assertEquals($expected, $this->storage->getBackupCodesState($user)); + } + + public function testGetBackupCodeDisabled(): void { + $user = $this->createMock(IUser::class); + + $codes = []; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + + $expected = [ + 'enabled' => false, + 'total' => 0, + 'used' => 0, + ]; + $this->assertEquals($expected, $this->storage->getBackupCodesState($user)); + } + + public function testValidateCode(): void { + $user = $this->createMock(IUser::class); + $code = new BackupCode(); + $code->setUsed(0); + $code->setCode('HASHEDVALUE'); + $codes = [ + $code, + ]; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + $this->hasher->expects($this->once()) + ->method('verify') + ->with('CHALLENGE', 'HASHEDVALUE', $this->anything()) + ->willReturn(true); + $this->mapper->expects($this->once()) + ->method('update') + ->with($code); + + $this->assertTrue($this->storage->validateCode($user, 'CHALLENGE')); + + $this->assertEquals(1, $code->getUsed()); + } + + public function testValidateUsedCode(): void { + $user = $this->createMock(IUser::class); + $code = new BackupCode(); + $code->setUsed('1'); + $code->setCode('HASHEDVALUE'); + $codes = [ + $code, + ]; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + $this->hasher->expects($this->never()) + ->method('verify'); + $this->mapper->expects($this->never()) + ->method('update'); + + $this->assertFalse($this->storage->validateCode($user, 'CHALLENGE')); + } + + public function testValidateCodeWithWrongHash(): void { + $user = $this->createMock(IUser::class); + $code = new BackupCode(); + $code->setUsed(0); + $code->setCode('HASHEDVALUE'); + $codes = [ + $code, + ]; + + $this->mapper->expects($this->once()) + ->method('getBackupCodes') + ->with($user) + ->willReturn($codes); + $this->hasher->expects($this->once()) + ->method('verify') + ->with('CHALLENGE', 'HASHEDVALUE') + ->willReturn(false); + $this->mapper->expects($this->never()) + ->method('update'); + + $this->assertFalse($this->storage->validateCode($user, 'CHALLENGE')); + } + + public function testDeleteCodes(): void { + $user = $this->createMock(IUser::class); + $this->mapper->expects($this->once()) + ->method('deleteCodes') + ->with($user); + + $this->storage->deleteCodes($user); + } +} |