diff options
Diffstat (limited to 'apps/twofactor_backupcodes/lib')
24 files changed, 719 insertions, 458 deletions
diff --git a/apps/twofactor_backupcodes/lib/Activity/Provider.php b/apps/twofactor_backupcodes/lib/Activity/Provider.php index 739ac9bbe13..eba38147bd7 100644 --- a/apps/twofactor_backupcodes/lib/Activity/Provider.php +++ b/apps/twofactor_backupcodes/lib/Activity/Provider.php @@ -1,28 +1,14 @@ <?php +declare(strict_types=1); + /** - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * Two-factor backup codes - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Activity; -use InvalidArgumentException; +use OCP\Activity\Exceptions\UnknownActivityException; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\Activity\IProvider; @@ -31,29 +17,21 @@ use OCP\L10N\IFactory as L10nFactory; class Provider implements IProvider { - /** @var L10nFactory */ - private $l10n; - - /** @var IURLGenerator */ - private $urlGenerator; - - /** @var IManager */ - private $activityManager; - /** * @param L10nFactory $l10n * @param IURLGenerator $urlGenerator * @param IManager $activityManager */ - public function __construct(L10nFactory $l10n, IURLGenerator $urlGenerator, IManager $activityManager) { - $this->urlGenerator = $urlGenerator; - $this->activityManager = $activityManager; - $this->l10n = $l10n; + public function __construct( + private L10nFactory $l10n, + private IURLGenerator $urlGenerator, + private IManager $activityManager, + ) { } - public function parse($language, IEvent $event, IEvent $previousEvent = null) { + public function parse($language, IEvent $event, ?IEvent $previousEvent = null): IEvent { if ($event->getApp() !== 'twofactor_backupcodes') { - throw new InvalidArgumentException(); + throw new UnknownActivityException(); } $l = $this->l10n->get('twofactor_backupcodes', $language); @@ -69,9 +47,8 @@ class Provider implements IProvider { } break; default: - throw new InvalidArgumentException(); + throw new UnknownActivityException(); } return $event; } - } diff --git a/apps/twofactor_backupcodes/lib/AppInfo/Application.php b/apps/twofactor_backupcodes/lib/AppInfo/Application.php index 050473f7efe..e9a1ec1c72c 100644 --- a/apps/twofactor_backupcodes/lib/AppInfo/Application.php +++ b/apps/twofactor_backupcodes/lib/AppInfo/Application.php @@ -1,54 +1,51 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\AppInfo; -use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper; +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCA\TwoFactorBackupCodes\Listener\ActivityPublisher; +use OCA\TwoFactorBackupCodes\Listener\ClearNotifications; +use OCA\TwoFactorBackupCodes\Listener\ProviderDisabled; +use OCA\TwoFactorBackupCodes\Listener\ProviderEnabled; +use OCA\TwoFactorBackupCodes\Listener\RegistryUpdater; +use OCA\TwoFactorBackupCodes\Listener\UserDeleted; +use OCA\TwoFactorBackupCodes\Notifications\Notifier; +use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; use OCP\AppFramework\App; -use OCP\Util; +use OCP\AppFramework\Bootstrap\IBootContext; +use OCP\AppFramework\Bootstrap\IBootstrap; +use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserUnregistered; +use OCP\User\Events\UserDeletedEvent; -class Application extends App { - public function __construct () { - parent::__construct('twofactor_backupcodes'); - } +class Application extends App implements IBootstrap { + public const APP_ID = 'twofactor_backupcodes'; - /** - * Register the different app parts - */ - public function register() { - $this->registerHooksAndEvents(); + public function __construct() { + parent::__construct(self::APP_ID); } - /** - * Register the hooks and events - */ - public function registerHooksAndEvents() { - Util::connectHook('OC_User', 'post_deleteUser', $this, 'deleteUser'); + public function register(IRegistrationContext $context): void { + $context->registerNotifierService(Notifier::class); + + $context->registerEventListener(CodesGenerated::class, ActivityPublisher::class); + $context->registerEventListener(CodesGenerated::class, RegistryUpdater::class); + $context->registerEventListener(CodesGenerated::class, ClearNotifications::class); + $context->registerEventListener(TwoFactorProviderForUserRegistered::class, ProviderEnabled::class); + $context->registerEventListener(TwoFactorProviderForUserUnregistered::class, ProviderDisabled::class); + $context->registerEventListener(UserDeletedEvent::class, UserDeleted::class); + + + $context->registerTwoFactorProvider(BackupCodesProvider::class); } - public function deleteUser($params) { - /** @var BackupCodeMapper $mapper */ - $mapper = $this->getContainer()->query(BackupCodeMapper::class); - $mapper->deleteCodesByUserId($params['uid']); + public function boot(IBootContext $context): void { } } diff --git a/apps/twofactor_backupcodes/lib/BackgroundJob/CheckBackupCodes.php b/apps/twofactor_backupcodes/lib/BackgroundJob/CheckBackupCodes.php new file mode 100644 index 00000000000..bc26cb260f4 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/BackgroundJob/CheckBackupCodes.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\BackgroundJob; + +use OC\Authentication\TwoFactorAuth\Manager; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\BackgroundJob\IJobList; +use OCP\BackgroundJob\QueuedJob; +use OCP\IUser; +use OCP\IUserManager; + +class CheckBackupCodes extends QueuedJob { + + /** @var Manager */ + private $twofactorManager; + + public function __construct( + ITimeFactory $timeFactory, + private IUserManager $userManager, + private IJobList $jobList, + Manager $twofactorManager, + private IRegistry $registry, + ) { + parent::__construct($timeFactory); + $this->twofactorManager = $twofactorManager; + } + + protected function run($argument) { + $this->userManager->callForSeenUsers(function (IUser $user): void { + if (!$user->isEnabled()) { + return; + } + + $providers = $this->registry->getProviderStates($user); + $isTwoFactorAuthenticated = $this->twofactorManager->isTwoFactorAuthenticated($user); + + if ($isTwoFactorAuthenticated && isset($providers['backup_codes']) && $providers['backup_codes'] === false) { + $this->jobList->add(RememberBackupCodesJob::class, ['uid' => $user->getUID()]); + } + }); + } +} diff --git a/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php new file mode 100644 index 00000000000..5e853479f0a --- /dev/null +++ b/apps/twofactor_backupcodes/lib/BackgroundJob/RememberBackupCodesJob.php @@ -0,0 +1,77 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\BackgroundJob; + +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\BackgroundJob\IJobList; +use OCP\BackgroundJob\TimedJob; +use OCP\IUserManager; +use OCP\Notification\IManager; + +class RememberBackupCodesJob extends TimedJob { + + public function __construct( + private IRegistry $registry, + private IUserManager $userManager, + ITimeFactory $timeFactory, + private IManager $notificationManager, + private IJobList $jobList, + ) { + parent::__construct($timeFactory); + $this->time = $timeFactory; + + $this->setInterval(60 * 60 * 24 * 14); + $this->setTimeSensitivity(self::TIME_INSENSITIVE); + } + + protected function run($argument) { + $uid = $argument['uid']; + $user = $this->userManager->get($uid); + + if ($user === null) { + // We can't run with an invalid user + $this->jobList->remove(self::class, $argument); + return; + } + + $providers = $this->registry->getProviderStates($user); + $state2fa = array_reduce($providers, function (bool $carry, bool $state) { + return $carry || $state; + }, false); + + /* + * If no provider is active or if the backup codes are already generate + * we can remove the job + */ + if ($state2fa === false || (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()) + ->setObject('create', 'codes') + ->setSubject('create_backupcodes'); + $this->notificationManager->markProcessed($notification); + + if (!$user->isEnabled()) { + // Don't recreate a notification for a user that can not read it + $this->jobList->remove(self::class, $argument); + return; + } + $notification->setDateTime($date); + $this->notificationManager->notify($notification); + } +} diff --git a/apps/twofactor_backupcodes/lib/Controller/SettingsController.php b/apps/twofactor_backupcodes/lib/Controller/SettingsController.php index 9b0b0fc57ba..effc058e05c 100644 --- a/apps/twofactor_backupcodes/lib/Controller/SettingsController.php +++ b/apps/twofactor_backupcodes/lib/Controller/SettingsController.php @@ -1,69 +1,44 @@ <?php +declare(strict_types=1); + /** - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Controller; use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; use OCP\IUserSession; class SettingsController extends Controller { - /** @var BackupCodeStorage */ - private $storage; - - /** @var IUserSession */ - private $userSession; - /** * @param string $appName * @param IRequest $request * @param BackupCodeStorage $storage * @param IUserSession $userSession */ - public function __construct($appName, IRequest $request, BackupCodeStorage $storage, IUserSession $userSession) { + public function __construct( + $appName, + IRequest $request, + private BackupCodeStorage $storage, + private IUserSession $userSession, + ) { parent::__construct($appName, $request); - $this->userSession = $userSession; - $this->storage = $storage; } /** - * @NoAdminRequired * @return JSONResponse */ - public function state() { - $user = $this->userSession->getUser(); - return $this->storage->getBackupCodesState($user); - } - - /** - * @NoAdminRequired - * @PasswordConfirmationRequired - * - * @return JSONResponse - */ - public function createCodes() { + #[NoAdminRequired] + #[PasswordConfirmationRequired] + public function createCodes(): JSONResponse { $user = $this->userSession->getUser(); $codes = $this->storage->createCodes($user); return new JSONResponse([ @@ -71,5 +46,4 @@ class SettingsController extends Controller { 'state' => $this->storage->getBackupCodesState($user), ]); } - } diff --git a/apps/twofactor_backupcodes/lib/Db/BackupCode.php b/apps/twofactor_backupcodes/lib/Db/BackupCode.php index cff515dd4aa..252b9b77faa 100644 --- a/apps/twofactor_backupcodes/lib/Db/BackupCode.php +++ b/apps/twofactor_backupcodes/lib/Db/BackupCode.php @@ -1,27 +1,13 @@ <?php + +declare(strict_types=1); + /** - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Db; - use OCP\AppFramework\Db\Entity; /** @@ -42,5 +28,4 @@ class BackupCode extends Entity { /** @var int */ protected $used; - } diff --git a/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php b/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php index 03fe7bb5ec8..fbbc3d0403c 100644 --- a/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php +++ b/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php @@ -1,33 +1,22 @@ <?php + +declare(strict_types=1); + /** - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Db; -use OCP\AppFramework\Db\Mapper; +use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; -class BackupCodeMapper extends Mapper { - +/** + * @template-extends QBMapper<BackupCode> + */ +class BackupCodeMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'twofactor_backupcodes'); } @@ -36,34 +25,28 @@ class BackupCodeMapper extends Mapper { * @param IUser $user * @return BackupCode[] */ - public function getBackupCodes(IUser $user) { + public function getBackupCodes(IUser $user): array { /* @var IQueryBuilder $qb */ $qb = $this->db->getQueryBuilder(); $qb->select('id', 'user_id', 'code', 'used') ->from('twofactor_backupcodes') ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID()))); - $result = $qb->execute(); - $rows = $result->fetchAll(); - $result->closeCursor(); - - return array_map(function ($row) { - return BackupCode::fromRow($row); - }, $rows); + return self::findEntities($qb); } /** * @param IUser $user */ - public function deleteCodes(IUser $user) { + public function deleteCodes(IUser $user): void { $this->deleteCodesByUserId($user->getUID()); } /** * @param string $uid */ - public function deleteCodesByUserId($uid) { + public function deleteCodesByUserId(string $uid): void { /* @var IQueryBuilder $qb */ $qb = $this->db->getQueryBuilder(); @@ -71,5 +54,4 @@ class BackupCodeMapper extends Mapper { ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid))); $qb->execute(); } - } diff --git a/apps/twofactor_backupcodes/lib/Event/CodesGenerated.php b/apps/twofactor_backupcodes/lib/Event/CodesGenerated.php new file mode 100644 index 00000000000..90dc6206f71 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Event/CodesGenerated.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Event; + +use OCP\EventDispatcher\Event; +use OCP\IUser; + +class CodesGenerated extends Event { + + public function __construct( + private IUser $user, + ) { + parent::__construct(); + } + + /** + * @return IUser + */ + public function getUser(): IUser { + return $this->user; + } +} diff --git a/apps/twofactor_backupcodes/lib/Listener/ActivityPublisher.php b/apps/twofactor_backupcodes/lib/Listener/ActivityPublisher.php new file mode 100644 index 00000000000..b52f1f2c098 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/ActivityPublisher.php @@ -0,0 +1,44 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Listener; + +use BadMethodCallException; +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCP\Activity\IManager; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; + +/** @template-implements IEventListener<CodesGenerated> */ +class ActivityPublisher implements IEventListener { + public function __construct( + private IManager $activityManager, + private LoggerInterface $logger, + ) { + } + + /** + * Push an event to the user's activity stream + */ + public function handle(Event $event): void { + if ($event instanceof CodesGenerated) { + $activity = $this->activityManager->generateEvent(); + $activity->setApp('twofactor_backupcodes') + ->setType('security') + ->setAuthor($event->getUser()->getUID()) + ->setAffectedUser($event->getUser()->getUID()) + ->setSubject('codes_generated'); + try { + $this->activityManager->publish($activity); + } catch (BadMethodCallException $e) { + $this->logger->error('Could not publish backup code creation activity', ['exception' => $e]); + } + } + } +} diff --git a/apps/twofactor_backupcodes/lib/Listener/ClearNotifications.php b/apps/twofactor_backupcodes/lib/Listener/ClearNotifications.php new file mode 100644 index 00000000000..46bb1583004 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/ClearNotifications.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Notification\IManager; + +/** @template-implements IEventListener<CodesGenerated> */ +class ClearNotifications implements IEventListener { + + public function __construct( + private IManager $manager, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof CodesGenerated)) { + return; + } + + $notification = $this->manager->createNotification(); + $notification->setApp('twofactor_backupcodes') + ->setUser($event->getUser()->getUID()) + ->setObject('create', 'codes'); + $this->manager->markProcessed($notification); + } +} diff --git a/apps/twofactor_backupcodes/lib/Listener/ProviderDisabled.php b/apps/twofactor_backupcodes/lib/Listener/ProviderDisabled.php new file mode 100644 index 00000000000..a8d51e55c1b --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/ProviderDisabled.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserUnregistered; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +/** @template-implements IEventListener<TwoFactorProviderForUserUnregistered> */ +class ProviderDisabled implements IEventListener { + + public function __construct( + private IRegistry $registry, + private IJobList $jobList, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof TwoFactorProviderForUserUnregistered)) { + return; + } + + $providers = $this->registry->getProviderStates($event->getUser()); + + // Loop over all providers. If all are disabled we remove the job + $state = array_reduce($providers, function (bool $carry, bool $enabled) { + return $carry || $enabled; + }, false); + + if ($state === false) { + $this->jobList->remove(RememberBackupCodesJob::class, ['uid' => $event->getUser()->getUID()]); + } + } +} diff --git a/apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php b/apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php new file mode 100644 index 00000000000..4ec510e7194 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/ProviderEnabled.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\BackgroundJob\RememberBackupCodesJob; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\Authentication\TwoFactorAuth\TwoFactorProviderForUserRegistered; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +/** @template-implements IEventListener<TwoFactorProviderForUserRegistered> */ +class ProviderEnabled implements IEventListener { + + public function __construct( + private IRegistry $registry, + private IJobList $jobList, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof TwoFactorProviderForUserRegistered)) { + 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/Listener/RegistryUpdater.php b/apps/twofactor_backupcodes/lib/Listener/RegistryUpdater.php new file mode 100644 index 00000000000..1cb07bd9805 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/RegistryUpdater.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; +use OCP\Authentication\TwoFactorAuth\IRegistry; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +/** @template-implements IEventListener<CodesGenerated> */ +class RegistryUpdater implements IEventListener { + + public function __construct( + private IRegistry $registry, + private BackupCodesProvider $provider, + ) { + } + + public function handle(Event $event): void { + if ($event instanceof CodesGenerated) { + $this->registry->enableProviderFor($this->provider, $event->getUser()); + } + } +} diff --git a/apps/twofactor_backupcodes/lib/Listener/UserDeleted.php b/apps/twofactor_backupcodes/lib/Listener/UserDeleted.php new file mode 100644 index 00000000000..72fd504b338 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Listener/UserDeleted.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Listener; + +use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\User\Events\UserDeletedEvent; + +/** @template-implements IEventListener<UserDeletedEvent> */ +class UserDeleted implements IEventListener { + + public function __construct( + private BackupCodeMapper $backupCodeMapper, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof UserDeletedEvent)) { + return; + } + + $this->backupCodeMapper->deleteCodes($event->getUser()); + } +} diff --git a/apps/twofactor_backupcodes/lib/Migration/CheckBackupCodes.php b/apps/twofactor_backupcodes/lib/Migration/CheckBackupCodes.php new file mode 100644 index 00000000000..9c0c676134b --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Migration/CheckBackupCodes.php @@ -0,0 +1,29 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Migration; + +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class CheckBackupCodes implements IRepairStep { + + public function __construct( + private IJobList $jobList, + ) { + } + + public function getName(): string { + return 'Add background job to check for backup codes'; + } + + public function run(IOutput $output) { + $this->jobList->add(\OCA\TwoFactorBackupCodes\BackgroundJob\CheckBackupCodes::class); + } +} diff --git a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607104347.php b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607104347.php index 9d369ae3a8d..ce752541bac 100644 --- a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607104347.php +++ b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607104347.php @@ -1,32 +1,17 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Migration; +use Doctrine\DBAL\Types\Types; use OCP\DB\ISchemaWrapper; -use Doctrine\DBAL\Types\Type; -use OCP\Migration\SimpleMigrationStep; use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; class Version1002Date20170607104347 extends SimpleMigrationStep { /** @@ -43,20 +28,20 @@ class Version1002Date20170607104347 extends SimpleMigrationStep { if (!$schema->hasTable('twofactor_backupcodes')) { $table = $schema->createTable('twofactor_backupcodes'); - $table->addColumn('id', Type::INTEGER, [ + $table->addColumn('id', Types::INTEGER, [ 'autoincrement' => true, 'notnull' => true, 'length' => 20, ]); - $table->addColumn('user_id', Type::STRING, [ + $table->addColumn('user_id', Types::STRING, [ 'notnull' => true, 'length' => 64, ]); - $table->addColumn('code', Type::STRING, [ + $table->addColumn('code', Types::STRING, [ 'notnull' => true, 'length' => 64, ]); - $table->addColumn('used', Type::INTEGER, [ + $table->addColumn('used', Types::INTEGER, [ 'notnull' => true, 'length' => 1, 'default' => 0, diff --git a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607113030.php b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607113030.php index 6895aa44a51..bed733cd413 100644 --- a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607113030.php +++ b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170607113030.php @@ -1,44 +1,27 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Migration; use OCP\DB\ISchemaWrapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; -use OCP\Migration\SimpleMigrationStep; use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; class Version1002Date20170607113030 extends SimpleMigrationStep { - /** @var IDBConnection */ - protected $connection; - /** * @param IDBConnection $connection */ - public function __construct(IDBConnection $connection) { - $this->connection = $connection; + public function __construct( + protected IDBConnection $connection, + ) { } /** diff --git a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php index 5cbbcb2ecca..2ca390e6edd 100644 --- a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php +++ b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170919123342.php @@ -1,32 +1,18 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Migration; use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; use OCP\DB\ISchemaWrapper; -use OCP\Migration\SimpleMigrationStep; use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; class Version1002Date20170919123342 extends SimpleMigrationStep { @@ -46,8 +32,8 @@ class Version1002Date20170919123342 extends SimpleMigrationStep { $column->setDefault(''); $column = $table->getColumn('used'); - if ($column->getType()->getName() !== Type::SMALLINT) { - $column->setType(Type::getType(Type::SMALLINT)); + if ($column->getType()->getName() !== Types::SMALLINT) { + $column->setType(Type::getType(Types::SMALLINT)); $column->setOptions(['length' => 6]); } diff --git a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170926101419.php b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170926101419.php index 0e19fe2a35e..d19fda49182 100644 --- a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170926101419.php +++ b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20170926101419.php @@ -1,4 +1,11 @@ <?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ namespace OCA\TwoFactorBackupCodes\Migration; use OCP\Migration\BigIntMigration; @@ -10,7 +17,7 @@ class Version1002Date20170926101419 extends BigIntMigration { /** * @return array Returns an array with the following structure - * ['table1' => ['column1', 'column2'], ...] + * ['table1' => ['column1', 'column2'], ...] * @since 13.0.0 */ protected function getColumnsByTable() { @@ -18,5 +25,4 @@ class Version1002Date20170926101419 extends BigIntMigration { 'twofactor_backupcodes' => ['id'], ]; } - } diff --git a/apps/twofactor_backupcodes/lib/Migration/Version1002Date20180821043638.php b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20180821043638.php new file mode 100644 index 00000000000..04d6d58c783 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Migration/Version1002Date20180821043638.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version1002Date20180821043638 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @return ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + $table = $schema->getTable('twofactor_backupcodes'); + + $table->getColumn('code')->setLength(128); + + return $schema; + } +} diff --git a/apps/twofactor_backupcodes/lib/Notifications/Notifier.php b/apps/twofactor_backupcodes/lib/Notifications/Notifier.php new file mode 100644 index 00000000000..e8144f52a56 --- /dev/null +++ b/apps/twofactor_backupcodes/lib/Notifications/Notifier.php @@ -0,0 +1,73 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\TwoFactorBackupCodes\Notifications; + +use OCP\IURLGenerator; +use OCP\L10N\IFactory; +use OCP\Notification\INotification; +use OCP\Notification\INotifier; +use OCP\Notification\UnknownNotificationException; + +class Notifier implements INotifier { + + public function __construct( + private IFactory $factory, + private IURLGenerator $url, + ) { + } + + /** + * Identifier of the notifier, only use [a-z0-9_] + * + * @return string + * @since 17.0.0 + */ + public function getID(): string { + return 'twofactor_backupcodes'; + } + + /** + * Human readable name describing the notifier + * + * @return string + * @since 17.0.0 + */ + public function getName(): string { + return $this->factory->get('twofactor_backupcodes')->t('Second-factor backup codes'); + } + + public function prepare(INotification $notification, string $languageCode): INotification { + if ($notification->getApp() !== 'twofactor_backupcodes') { + // Not my app => throw + throw new UnknownNotificationException(); + } + + // 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 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.') + ); + + $notification->setLink($this->url->linkToRouteAbsolute('settings.PersonalSettings.index', ['section' => 'security'])); + + $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/password.svg'))); + + return $notification; + + default: + // Unknown subject => Unknown notification => throw + throw new UnknownNotificationException(); + } + } +} diff --git a/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php b/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php index 5c5500862e6..c521329e203 100644 --- a/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php +++ b/apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php @@ -1,59 +1,34 @@ <?php +declare(strict_types=1); + /** - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Provider; use OC\App\AppManager; use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage; -use OCP\Authentication\TwoFactorAuth\IProvider; +use OCA\TwoFactorBackupCodes\Settings\Personal; +use OCP\AppFramework\Services\IInitialState; +use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin; +use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; +use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings; use OCP\IL10N; use OCP\IUser; -use OCP\Template; - -class BackupCodesProvider implements IProvider { - - /** @var string */ - private $appName; - - /** @var BackupCodeStorage */ - private $storage; - - /** @var IL10N */ - private $l10n; - - /** @var AppManager */ - private $appManager; - - /** - * @param string $appName - * @param BackupCodeStorage $storage - * @param IL10N $l10n - * @param AppManager $appManager - */ - public function __construct($appName, BackupCodeStorage $storage, IL10N $l10n, AppManager $appManager) { - $this->appName = $appName; - $this->l10n = $l10n; - $this->storage = $storage; - $this->appManager = $appManager; +use OCP\Template\ITemplate; +use OCP\Template\ITemplateManager; + +class BackupCodesProvider implements IDeactivatableByAdmin, IProvidesPersonalSettings { + public function __construct( + private string $appName, + private BackupCodeStorage $storage, + private IL10N $l10n, + private AppManager $appManager, + private IInitialState $initialState, + private ITemplateManager $templateManager, + ) { } /** @@ -61,7 +36,7 @@ class BackupCodesProvider implements IProvider { * * @return string */ - public function getId() { + public function getId(): string { return 'backup_codes'; } @@ -70,7 +45,7 @@ class BackupCodesProvider implements IProvider { * * @return string */ - public function getDisplayName() { + public function getDisplayName(): string { return $this->l10n->t('Backup code'); } @@ -79,7 +54,7 @@ class BackupCodesProvider implements IProvider { * * @return string */ - public function getDescription() { + public function getDescription(): string { return $this->l10n->t('Use backup code'); } @@ -87,11 +62,10 @@ class BackupCodesProvider implements IProvider { * Get the template for rending the 2FA provider view * * @param IUser $user - * @return Template + * @return ITemplate */ - public function getTemplate(IUser $user) { - $tmpl = new Template('twofactor_backupcodes', 'challenge'); - return $tmpl; + public function getTemplate(IUser $user): ITemplate { + return $this->templateManager->getTemplate('twofactor_backupcodes', 'challenge'); } /** @@ -99,8 +73,9 @@ class BackupCodesProvider implements IProvider { * * @param IUser $user * @param string $challenge + * @return bool */ - public function verifyChallenge(IUser $user, $challenge) { + public function verifyChallenge(IUser $user, string $challenge): bool { return $this->storage->validateCode($user, $challenge); } @@ -110,7 +85,7 @@ class BackupCodesProvider implements IProvider { * @param IUser $user * @return boolean */ - public function isTwoFactorAuthEnabledForUser(IUser $user) { + public function isTwoFactorAuthEnabledForUser(IUser $user): bool { return $this->storage->hasBackupCodes($user); } @@ -125,8 +100,8 @@ class BackupCodesProvider implements IProvider { * @param IUser $user * @return boolean */ - public function isActive(IUser $user) { - $appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function($appId) { + public function isActive(IUser $user): bool { + $appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function ($appId) { return $appId !== $this->appName; }); foreach ($appIds as $appId) { @@ -138,4 +113,18 @@ class BackupCodesProvider implements IProvider { return false; } + /** + * @param IUser $user + * + * @return IPersonalProviderSettings + */ + public function getPersonalSettings(IUser $user): IPersonalProviderSettings { + $state = $this->storage->getBackupCodesState($user); + $this->initialState->provideInitialState('state', $state); + return new Personal(); + } + + public function disableFor(IUser $user): void { + $this->storage->deleteCodes($user); + } } diff --git a/apps/twofactor_backupcodes/lib/Service/BackupCodeStorage.php b/apps/twofactor_backupcodes/lib/Service/BackupCodeStorage.php index 84bf54d3379..7dd6b3949e2 100644 --- a/apps/twofactor_backupcodes/lib/Service/BackupCodeStorage.php +++ b/apps/twofactor_backupcodes/lib/Service/BackupCodeStorage.php @@ -1,76 +1,38 @@ <?php +declare(strict_types=1); + /** - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\TwoFactorBackupCodes\Service; -use BadMethodCallException; use OCA\TwoFactorBackupCodes\Db\BackupCode; use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper; -use OCP\Activity\IManager; -use OCP\ILogger; +use OCA\TwoFactorBackupCodes\Event\CodesGenerated; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; class BackupCodeStorage { - private static $CODE_LENGTH = 16; - /** @var BackupCodeMapper */ - private $mapper; - - /** @var IHasher */ - private $hasher; - - /** @var ISecureRandom */ - private $random; - - /** @var IManager */ - private $activityManager; - - /** @var ILogger */ - private $logger; - - /** - * @param BackupCodeMapper $mapper - * @param ISecureRandom $random - * @param IHasher $hasher - * @param IManager $activityManager - * @param ILogger $logger - */ - public function __construct(BackupCodeMapper $mapper, ISecureRandom $random, IHasher $hasher, - IManager $activityManager, ILogger $logger) { - $this->mapper = $mapper; - $this->hasher = $hasher; - $this->random = $random; - $this->activityManager = $activityManager; - $this->logger = $logger; + public function __construct( + private BackupCodeMapper $mapper, + private ISecureRandom $random, + private IHasher $hasher, + private IEventDispatcher $eventDispatcher, + ) { } /** * @param IUser $user + * @param int $number * @return string[] */ - public function createCodes(IUser $user, $number = 10) { + public function createCodes(IUser $user, int $number = 10): array { $result = []; // Delete existing ones @@ -78,7 +40,7 @@ class BackupCodeStorage { $uid = $user->getUID(); foreach (range(1, min([$number, 20])) as $i) { - $code = $this->random->generate(self::$CODE_LENGTH, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); + $code = $this->random->generate(self::$CODE_LENGTH, ISecureRandom::CHAR_HUMAN_READABLE); $dbCode = new BackupCode(); $dbCode->setUserId($uid); @@ -86,40 +48,19 @@ class BackupCodeStorage { $dbCode->setUsed(0); $this->mapper->insert($dbCode); - array_push($result, $code); + $result[] = $code; } - $this->publishEvent($user, 'codes_generated'); + $this->eventDispatcher->dispatchTyped(new CodesGenerated($user)); return $result; } /** - * Push an event the user's activity stream - * - * @param IUser $user - * @param string $event - */ - private function publishEvent(IUser $user, $event) { - $activity = $this->activityManager->generateEvent(); - $activity->setApp('twofactor_backupcodes') - ->setType('security') - ->setAuthor($user->getUID()) - ->setAffectedUser($user->getUID()) - ->setSubject($event); - try { - $this->activityManager->publish($activity); - } catch (BadMethodCallException $e) { - $this->logger->warning('could not publish backup code creation activity', ['app' => 'twofactor_backupcodes']); - $this->logger->logException($e, ['app' => 'twofactor_backupcodes']); - } - } - - /** * @param IUser $user * @return bool */ - public function hasBackupCodes(IUser $user) { + public function hasBackupCodes(IUser $user): bool { $codes = $this->mapper->getBackupCodes($user); return count($codes) > 0; } @@ -128,12 +69,12 @@ class BackupCodeStorage { * @param IUser $user * @return array */ - public function getBackupCodesState(IUser $user) { + public function getBackupCodesState(IUser $user): array { $codes = $this->mapper->getBackupCodes($user); $total = count($codes); $used = 0; - array_walk($codes, function (BackupCode $code) use (&$used) { - if (1 === (int) $code->getUsed()) { + array_walk($codes, function (BackupCode $code) use (&$used): void { + if ((int)$code->getUsed() === 1) { $used++; } }); @@ -149,11 +90,11 @@ class BackupCodeStorage { * @param string $code * @return bool */ - public function validateCode(IUser $user, $code) { + public function validateCode(IUser $user, string $code): bool { $dbCodes = $this->mapper->getBackupCodes($user); foreach ($dbCodes as $dbCode) { - if (0 === (int) $dbCode->getUsed() && $this->hasher->verify($code, $dbCode->getCode())) { + if ((int)$dbCode->getUsed() === 0 && $this->hasher->verify($code, $dbCode->getCode())) { $dbCode->setUsed(1); $this->mapper->update($dbCode); return true; @@ -162,4 +103,7 @@ class BackupCodeStorage { return false; } + public function deleteCodes(IUser $user): void { + $this->mapper->deleteCodes($user); + } } diff --git a/apps/twofactor_backupcodes/lib/Settings/Personal.php b/apps/twofactor_backupcodes/lib/Settings/Personal.php index eb28dacb42b..e03c3d303db 100644 --- a/apps/twofactor_backupcodes/lib/Settings/Personal.php +++ b/apps/twofactor_backupcodes/lib/Settings/Personal.php @@ -1,82 +1,21 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\TwoFactorBackupCodes\Settings; +use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; +use OCP\Server; +use OCP\Template\ITemplate; +use OCP\Template\ITemplateManager; -use OCA\TwoFactorBackupCodes\AppInfo\Application; -use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; -use OCP\AppFramework\Http\TemplateResponse; -use OCP\IUserSession; -use OCP\Settings\ISettings; - -class Personal implements ISettings { - - /** @var Application */ - private $app; - /** @var BackupCodesProvider */ - private $provider; - /** @var IUserSession */ - private $userSession; - - public function __construct(Application $app, BackupCodesProvider $provider, IUserSession $userSession) { - $this->app = $app; - $this->provider = $provider; - $this->userSession = $userSession; - } - - /** - * @return TemplateResponse returns the instance with all parameters set, ready to be rendered - * @since 9.1 - */ - public function getForm() { - $templateOwner = 'settings'; - $templateName = 'settings/empty'; - if ($this->provider->isActive($this->userSession->getUser())) { - $templateOwner = $this->app->getContainer()->getAppName(); - $templateName = 'personal'; - } - - return new TemplateResponse($templateOwner, $templateName, [], ''); - } - - /** - * @return string the section ID, e.g. 'sharing' - * @since 9.1 - */ - public function getSection() { - return 'security'; - } - - /** - * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. - * - * E.g.: 70 - * @since 9.1 - */ - public function getPriority() { - return 40; +class Personal implements IPersonalProviderSettings { + public function getBody(): ITemplate { + return Server::get(ITemplateManager::class)->getTemplate('twofactor_backupcodes', 'personal'); } } |