From 956fe1b86769c1a8a380a61ba72441f0e334e36a Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Sat, 29 Sep 2018 20:56:23 +0200 Subject: [PATCH] Generate backups code notification if not enable but 2fa is Generate a notification to generate backup codes if you enable an other 2FA provider but backup codes are not yet generated. * Add event listner * Insert background job * Background job tests and emits notification every 2 weeks * If the backup codes are generated the next run will remove the job Signed-off-by: Roeland Jago Douma --- .../composer/composer/autoload_classmap.php | 3 + .../composer/composer/autoload_static.php | 3 + .../lib/AppInfo/Application.php | 28 ++++ .../BackgroundJob/RememberBackupCodesJob.php | 92 +++++++++++ .../lib/Listener/ProviderEnabled.php | 61 +++++++ .../lib/Notifications/Notifier.php | 64 ++++++++ .../RememberBackupCodesJobTest.php | 153 ++++++++++++++++++ .../Unit/Listener/ProviderEnabledTest.php | 107 ++++++++++++ .../tests/Unit/Notification/NotifierTest.php | 120 ++++++++++++++ .../TwoFactorAuth/RegistryTest.php | 28 +++- 10 files changed, 658 insertions(+), 1 deletion(-) create mode 100644 apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php create mode 100644 apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php create mode 100644 apps/twofactor_backupcodes/lib/Notifications/Notifier.php create mode 100644 apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php create mode 100644 apps/twofactor_backupcodes/tests/Unit/Listener/ProviderEnabledTest.php create mode 100644 apps/twofactor_backupcodes/tests/Unit/Notification/NotifierTest.php diff --git a/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php b/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php index d21fc1ab77..12da21df05 100644 --- a/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php +++ b/apps/twofactor_backupcodes/composer/composer/autoload_classmap.php @@ -8,18 +8,21 @@ $baseDir = $vendorDir; return array( 'OCA\\TwoFactorBackupCodes\\Activity\\Provider' => $baseDir . '/../lib/Activity/Provider.php', 'OCA\\TwoFactorBackupCodes\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', + 'OCA\\TwoFactorBackupCodes\\BackgroundJob\\RememberBackupCodesJob' => $baseDir . '/../lib/BackgroundJob/RememberBackupCodesJob.php', 'OCA\\TwoFactorBackupCodes\\Controller\\SettingsController' => $baseDir . '/../lib/Controller/SettingsController.php', 'OCA\\TwoFactorBackupCodes\\Db\\BackupCode' => $baseDir . '/../lib/Db/BackupCode.php', 'OCA\\TwoFactorBackupCodes\\Db\\BackupCodeMapper' => $baseDir . '/../lib/Db/BackupCodeMapper.php', 'OCA\\TwoFactorBackupCodes\\Event\\CodesGenerated' => $baseDir . '/../lib/Event/CodesGenerated.php', 'OCA\\TwoFactorBackupCodes\\Listener\\ActivityPublisher' => $baseDir . '/../lib/Listener/ActivityPublisher.php', 'OCA\\TwoFactorBackupCodes\\Listener\\IListener' => $baseDir . '/../lib/Listener/IListener.php', + 'OCA\\TwoFactorBackupCodes\\Listener\\ProviderEnabled' => $baseDir . '/../lib/Listener/ProviderEnabled.php', 'OCA\\TwoFactorBackupCodes\\Listener\\RegistryUpdater' => $baseDir . '/../lib/Listener/RegistryUpdater.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170607104347' => $baseDir . '/../lib/Migration/Version1002Date20170607104347.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170607113030' => $baseDir . '/../lib/Migration/Version1002Date20170607113030.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170919123342' => $baseDir . '/../lib/Migration/Version1002Date20170919123342.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170926101419' => $baseDir . '/../lib/Migration/Version1002Date20170926101419.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20180821043638' => $baseDir . '/../lib/Migration/Version1002Date20180821043638.php', + 'OCA\\TwoFactorBackupCodes\\Notifications\\Notifier' => $baseDir . '/../lib/Notifications/Notifier.php', 'OCA\\TwoFactorBackupCodes\\Provider\\BackupCodesProvider' => $baseDir . '/../lib/Provider/BackupCodesProvider.php', 'OCA\\TwoFactorBackupCodes\\Service\\BackupCodeStorage' => $baseDir . '/../lib/Service/BackupCodeStorage.php', 'OCA\\TwoFactorBackupCodes\\Settings\\Personal' => $baseDir . '/../lib/Settings/Personal.php', diff --git a/apps/twofactor_backupcodes/composer/composer/autoload_static.php b/apps/twofactor_backupcodes/composer/composer/autoload_static.php index e1b9fb9007..350ef5d0b9 100644 --- a/apps/twofactor_backupcodes/composer/composer/autoload_static.php +++ b/apps/twofactor_backupcodes/composer/composer/autoload_static.php @@ -23,18 +23,21 @@ class ComposerStaticInitTwoFactorBackupCodes public static $classMap = array ( 'OCA\\TwoFactorBackupCodes\\Activity\\Provider' => __DIR__ . '/..' . '/../lib/Activity/Provider.php', 'OCA\\TwoFactorBackupCodes\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', + 'OCA\\TwoFactorBackupCodes\\BackgroundJob\\RememberBackupCodesJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/RememberBackupCodesJob.php', 'OCA\\TwoFactorBackupCodes\\Controller\\SettingsController' => __DIR__ . '/..' . '/../lib/Controller/SettingsController.php', 'OCA\\TwoFactorBackupCodes\\Db\\BackupCode' => __DIR__ . '/..' . '/../lib/Db/BackupCode.php', 'OCA\\TwoFactorBackupCodes\\Db\\BackupCodeMapper' => __DIR__ . '/..' . '/../lib/Db/BackupCodeMapper.php', 'OCA\\TwoFactorBackupCodes\\Event\\CodesGenerated' => __DIR__ . '/..' . '/../lib/Event/CodesGenerated.php', 'OCA\\TwoFactorBackupCodes\\Listener\\ActivityPublisher' => __DIR__ . '/..' . '/../lib/Listener/ActivityPublisher.php', 'OCA\\TwoFactorBackupCodes\\Listener\\IListener' => __DIR__ . '/..' . '/../lib/Listener/IListener.php', + 'OCA\\TwoFactorBackupCodes\\Listener\\ProviderEnabled' => __DIR__ . '/..' . '/../lib/Listener/ProviderEnabled.php', 'OCA\\TwoFactorBackupCodes\\Listener\\RegistryUpdater' => __DIR__ . '/..' . '/../lib/Listener/RegistryUpdater.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170607104347' => __DIR__ . '/..' . '/../lib/Migration/Version1002Date20170607104347.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170607113030' => __DIR__ . '/..' . '/../lib/Migration/Version1002Date20170607113030.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170919123342' => __DIR__ . '/..' . '/../lib/Migration/Version1002Date20170919123342.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20170926101419' => __DIR__ . '/..' . '/../lib/Migration/Version1002Date20170926101419.php', 'OCA\\TwoFactorBackupCodes\\Migration\\Version1002Date20180821043638' => __DIR__ . '/..' . '/../lib/Migration/Version1002Date20180821043638.php', + 'OCA\\TwoFactorBackupCodes\\Notifications\\Notifier' => __DIR__ . '/..' . '/../lib/Notifications/Notifier.php', 'OCA\\TwoFactorBackupCodes\\Provider\\BackupCodesProvider' => __DIR__ . '/..' . '/../lib/Provider/BackupCodesProvider.php', 'OCA\\TwoFactorBackupCodes\\Service\\BackupCodeStorage' => __DIR__ . '/..' . '/../lib/Service/BackupCodeStorage.php', 'OCA\\TwoFactorBackupCodes\\Settings\\Personal' => __DIR__ . '/..' . '/../lib/Settings/Personal.php', diff --git a/apps/twofactor_backupcodes/lib/AppInfo/Application.php b/apps/twofactor_backupcodes/lib/AppInfo/Application.php index d2541d8762..1af114a279 100644 --- a/apps/twofactor_backupcodes/lib/AppInfo/Application.php +++ b/apps/twofactor_backupcodes/lib/AppInfo/Application.php @@ -29,8 +29,14 @@ use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper; use OCA\TwoFactorBackupCodes\Event\CodesGenerated; use OCA\TwoFactorBackupCodes\Listener\ActivityPublisher; use OCA\TwoFactorBackupCodes\Listener\IListener; +use OCA\TwoFactorBackupCodes\Listener\ProviderEnabled; use OCA\TwoFactorBackupCodes\Listener\RegistryUpdater; +use OCA\TwoFactorBackupCodes\Notifications\Notifier; use OCP\AppFramework\App; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\RegistryEvent; +use OCP\IL10N; +use OCP\Notification\IManager; use OCP\Util; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -44,6 +50,7 @@ class Application extends App { */ public function register() { $this->registerHooksAndEvents(); + $this->registerNotification(); } /** @@ -66,6 +73,27 @@ class Application extends App { $listener->handle($event); } }); + + $eventDispatcher->addListener(IRegistry::EVENT_PROVIDER_ENABLED, function(RegistryEvent $event) use ($container) { + /** @var IListener $listener */ + $listener = $container->query(ProviderEnabled::class); + $listener->handle($event); + }); + } + + public function registerNotification() { + $container = $this->getContainer(); + /** @var IManager $manager */ + $manager = $container->query(IManager::class); + $manager->registerNotifier( + function() use ($container) { + return $container->query(Notifier::class); + }, + function () use ($container) { + $l = $container->query(IL10N::class); + return ['id' => 'twofactor_backupcodes', 'name' => $l->t('Second-factor backup codes')]; + } + ); } public function deleteUser($params) { diff --git a/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php new file mode 100644 index 0000000000..1f227061fe --- /dev/null +++ b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php @@ -0,0 +1,92 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\TwoFactorBackupCodes\BackgroundJob; + +use OC\BackgroundJob\TimedJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\BackgroundJob\IJobList; +use OCP\IUserManager; +use OCP\Notification\IManager; + +class RememberBackupCodesJob extends TimedJob { + + /** @var IRegistry */ + private $registry; + + /** @var IUserManager */ + private $userManager; + + /** @var ITimeFactory */ + private $time; + + /** @var IManager */ + private $notificationManager; + + /** @var IJobList */ + private $jobList; + + public function __construct(IRegistry $registry, + IUserManager $userManager, + ITimeFactory $timeFactory, + IManager $notificationManager, + IJobList $jobList) { + $this->registry = $registry; + $this->userManager = $userManager; + $this->time = $timeFactory; + $this->notificationManager = $notificationManager; + $this->jobList = $jobList; + + $this->setInterval(60*60*24*14); + } + + protected function run($argument) { + $uid = $argument['uid']; + $user = $this->userManager->get($uid); + + if ($user === null) { + // We can't run with an invalid user + return; + } + + $providers = $this->registry->getProviderStates($user); + if (isset($providers['backup_codes']) && $providers['backup_codes'] === true) { + // Backup codes already generated lets remove this job + $this->jobList->remove(self::class, $argument); + return; + } + + $date = new \DateTime(); + $date->setTimestamp($this->time->getTime()); + + $notification = $this->notificationManager->createNotification(); + $notification->setApp('twofactor_backupcodes') + ->setUser($user->getUID()) + ->setDateTime($date) + ->setObject('create', 'codes') + ->setSubject('create_backupcodes'); + $this->notificationManager->notify($notification); + } +} diff --git a/apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php b/apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php new file mode 100644 index 0000000000..48cbef66f1 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php @@ -0,0 +1,61 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\RegistryEvent; +use OCP\BackgroundJob\IJobList; +use Symfony\Component\EventDispatcher\Event; + +class ProviderEnabled implements IListener { + + /** @var IRegistry */ + private $registry; + + /** @var IJobList */ + private $jobList; + + public function __construct(IRegistry $registry, + IJobList $jobList) { + $this->registry = $registry; + $this->jobList = $jobList; + } + + public function handle(Event $event) { + if (!($event instanceof RegistryEvent)) { + return; + } + + $providers = $this->registry->getProviderStates($event->getUser()); + if (isset($providers['backup_codes']) && $providers['backup_codes'] === true) { + // Backup codes already generated nothing to do here + return; + } + + $this->jobList->add(RememberBackupCodesJob::class, ['uid' => $event->getUser()->getUID()]); + } + +} diff --git a/apps/twofactor_backupcodes/lib/Notifications/Notifier.php b/apps/twofactor_backupcodes/lib/Notifications/Notifier.php new file mode 100644 index 0000000000..3d5fedd93e --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Notifications/Notifier.php @@ -0,0 +1,64 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\TwoFactorBackupCodes\Notifications; + +use OCP\L10N\IFactory; +use OCP\Notification\INotification; +use OCP\Notification\INotifier; + +class Notifier implements INotifier { + + /** @var IFactory */ + private $factory; + + public function __construct(IFactory $factory) { + $this->factory = $factory; + } + + public function prepare(INotification $notification, $languageCode) { + if ($notification->getApp() !== 'twofactor_backupcodes') { + // Not my app => throw + throw new \InvalidArgumentException(); + } + + // Read the language from the notification + $l = $this->factory->get('twofactor_backupcodes', $languageCode); + + switch ($notification->getSubject()) { + case 'create_backupcodes': + $notification->setParsedSubject( + $l->t('Generate backup codes') + )->setParsedMessage( + $l->t('You have enabled two-factor authentication but have not yet generated backup codes. Be sure to do this in case you lose access to your second factor.') + ); + return $notification; + + default: + // Unknown subject => Unknown notification => throw + throw new \InvalidArgumentException(); + } + } + +} 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 0000000000..0e23e032bd --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/BackgroundJob/RememberBackupCodesJobTest.php @@ -0,0 +1,153 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +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 Test\TestCase; + +class RememberBackupCodesJobTest extends TestCase { + + /** @var IRegistry|\PHPUnit\Framework\MockObject\MockObject */ + private $registry; + + /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */ + private $userManager; + + /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $time; + + /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */ + private $notificationManager; + + /** @var IJobList|\PHPUnit\Framework\MockObject\MockObject */ + private $jobList; + + /** @var RememberBackupCodesJob */ + private $job; + + public function setUp() { + 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() { + $this->userManager->method('get') + ->with('invalidUID') + ->willReturn(null); + + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->invokePrivate($this->job, 'run', [['uid' => 'invalidUID']]); + } + + public function testBackupCodesGenerated() { + $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' => true + ]); + + $this->jobList->expects($this->once()) + ->method('remove') + ->with( + RememberBackupCodesJob::class, + ['uid' => 'validUID'] + ); + + $this->notificationManager->expects($this->never()) + ->method($this->anything()); + + $this->invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } + + public function testNotificationSend() { + $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 + ]); + + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $date = new \DateTime(); + $date->setTimestamp($this->time->getTime()); + + $this->notificationManager->method('createNotification') + ->willReturn(\OC::$server->query(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'; + })); + + $this->invokePrivate($this->job, 'run', [['uid' => 'validUID']]); + } +} 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 0000000000..c824ad8e87 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Listener/ProviderEnabledTest.php @@ -0,0 +1,107 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +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\RegistryEvent; +use OCP\BackgroundJob\IJobList; +use OCP\IUser; +use Symfony\Component\EventDispatcher\Event; +use Test\TestCase; + +class ProviderEnabledTest extends TestCase { + + /** @var IRegistry|\PHPUnit\Framework\MockObject\MockObject */ + private $registy; + + /** @var IJobList|\PHPUnit\Framework\MockObject\MockObject */ + private $jobList; + + /** @var ProviderEnabled */ + private $listener; + + protected function setUp() { + 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() { + $event = $this->createMock(Event::class); + $this->jobList->expects($this->never()) + ->method($this->anything()); + + $this->listener->handle($event); + } + + public function testHandleCodesGeneratedEventAlraedyBackupcodes() { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(RegistryEvent::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() { + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('myUID'); + $event = $this->createMock(RegistryEvent::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/Notification/NotifierTest.php b/apps/twofactor_backupcodes/tests/Unit/Notification/NotifierTest.php new file mode 100644 index 0000000000..508fa453e1 --- /dev/null +++ b/apps/twofactor_backupcodes/tests/Unit/Notification/NotifierTest.php @@ -0,0 +1,120 @@ + + * + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\TwoFactorBackupCodes\Tests\Unit\Notification; + +use OCA\TwoFactorBackupCodes\Notifications\Notifier; +use OCP\IL10N; +use OCP\L10N\IFactory; +use OCP\Notification\INotification; +use Test\TestCase; + +class NotifierTest extends TestCase { + /** @var Notifier */ + protected $notifier; + + /** @var IFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $factory; + /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */ + protected $l; + + protected function setUp() { + 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->factory->expects($this->any()) + ->method('get') + ->willReturn($this->l); + + $this->notifier = new Notifier( + $this->factory + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testPrepareWrongApp() { + /** @var INotification|\PHPUnit_Framework_MockObject_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'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testPrepareWrongSubject() { + /** @var INotification|\PHPUnit_Framework_MockObject_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() { + /** @var \OCP\Notification\INotification|\PHPUnit_Framework_MockObject_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 have enabled two-factor authentication but have not yet generated backup codes. Be sure to do this in case you lose access to your second factor.') + ->willReturnSelf(); + + $return = $this->notifier->prepare($notification, 'nl'); + $this->assertEquals($notification, $return); + } +} diff --git a/tests/lib/Authentication/TwoFactorAuth/RegistryTest.php b/tests/lib/Authentication/TwoFactorAuth/RegistryTest.php index 3d2941e009..08498738fa 100644 --- a/tests/lib/Authentication/TwoFactorAuth/RegistryTest.php +++ b/tests/lib/Authentication/TwoFactorAuth/RegistryTest.php @@ -27,8 +27,11 @@ namespace Test\Authentication\TwoFactorAuth; use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao; use OC\Authentication\TwoFactorAuth\Registry; use OCP\Authentication\TwoFactorAuth\IProvider; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\RegistryEvent; use OCP\IUser; use PHPUnit_Framework_MockObject_MockObject; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; class RegistryTest extends TestCase { @@ -39,12 +42,16 @@ class RegistryTest extends TestCase { /** @var Registry */ private $registry; + /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $dispatcher; + protected function setUp() { parent::setUp(); $this->dao = $this->createMock(ProviderUserAssignmentDao::class); + $this->dispatcher = $this->createMock(EventDispatcherInterface::class); - $this->registry = new Registry($this->dao); + $this->registry = new Registry($this->dao, $this->dispatcher); } public function testGetProviderStates() { @@ -68,6 +75,15 @@ class RegistryTest extends TestCase { $this->dao->expects($this->once())->method('persist')->with('p1', 'user123', true); + $this->dispatcher->expects($this->once()) + ->method('dispatch') + ->with( + $this->equalTo(IRegistry::EVENT_PROVIDER_ENABLED), + $this->callback(function(RegistryEvent $e) use ($user, $provider) { + return $e->getUser() === $user && $e->getProvider() === $provider; + }) + ); + $this->registry->enableProviderFor($provider, $user); } @@ -79,6 +95,16 @@ class RegistryTest extends TestCase { $this->dao->expects($this->once())->method('persist')->with('p1', 'user123', false); + + $this->dispatcher->expects($this->once()) + ->method('dispatch') + ->with( + $this->equalTo(IRegistry::EVENT_PROVIDER_DISABLED), + $this->callback(function(RegistryEvent $e) use ($user, $provider) { + return $e->getUser() === $user && $e->getProvider() === $provider; + }) + ); + $this->registry->disableProviderFor($provider, $user); }