diff options
author | Daniel Kesselberg <mail@danielkesselberg.de> | 2022-09-13 20:11:28 +0300 |
---|---|---|
committer | Daniel Kesselberg <mail@danielkesselberg.de> | 2022-09-13 23:08:42 +0300 |
commit | f1a24d038ece5d83e6dd73cc1e4fa0df664489fb (patch) | |
tree | 19d043fad3b557749d3e1d40eb29e048efd31017 | |
parent | f1d274eed6952849eb032df4122f0a05bd8eea64 (diff) |
Add command to cleanup topt secretsdebt/noid/cleanup
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
-rw-r--r-- | appinfo/info.xml | 5 | ||||
-rw-r--r-- | lib/AppInfo/Application.php | 3 | ||||
-rw-r--r-- | lib/Command/CleanUp.php | 106 | ||||
-rw-r--r-- | lib/Db/TotpSecretMapper.php | 13 | ||||
-rw-r--r-- | lib/Listener/UserDeleted.php | 58 |
5 files changed, 184 insertions, 1 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index fabf4ca..9cc1aeb 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ <name>Two-Factor TOTP Provider</name> <summary>TOTP two-factor provider</summary> <description>A Two-Factor-Auth Provider for TOTP (RFC 6238)</description> - <version>6.4.0-alpha.1</version> + <version>6.4.0-alpha.2</version> <licence>agpl</licence> <author>Christoph Wurst</author> <namespace>TwoFactorTOTP</namespace> @@ -23,6 +23,9 @@ <two-factor-providers> <provider>OCA\TwoFactorTOTP\Provider\TotpProvider</provider> </two-factor-providers> + <commands> + <command>OCA\TwoFactorTOTP\Command\CleanUp</command> + </commands> <activity> <settings> <setting>OCA\TwoFactorTOTP\Activity\Setting</setting> diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index ff5f7f4..d5aeffc 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -27,12 +27,14 @@ use OCA\TwoFactorTOTP\Event\DisabledByAdmin; use OCA\TwoFactorTOTP\Event\StateChanged; use OCA\TwoFactorTOTP\Listener\StateChangeActivity; use OCA\TwoFactorTOTP\Listener\StateChangeRegistryUpdater; +use OCA\TwoFactorTOTP\Listener\UserDeleted; use OCA\TwoFactorTOTP\Service\ITotp; use OCA\TwoFactorTOTP\Service\Totp; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\User\Events\UserDeletedEvent; class Application extends App implements IBootstrap { public const APP_ID = 'twofactor_totp'; @@ -49,6 +51,7 @@ class Application extends App implements IBootstrap { $context->registerEventListener(StateChanged::class, StateChangeActivity::class); $context->registerEventListener(StateChanged::class, StateChangeRegistryUpdater::class); $context->registerEventListener(DisabledByAdmin::class, StateChangeActivity::class); + $context->registerEventListener(UserDeletedEvent::class, UserDeleted::class); } public function boot(IBootContext $context): void { diff --git a/lib/Command/CleanUp.php b/lib/Command/CleanUp.php new file mode 100644 index 0000000..a68fb1f --- /dev/null +++ b/lib/Command/CleanUp.php @@ -0,0 +1,106 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Daniel Kesselberg <mail@danielkesselberg.de> + * + * @author Daniel Kesselberg <mail@danielkesselberg.de> + * + * @license AGPL-3.0-or-later + * + * 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/> + * + */ + +namespace OCA\TwoFactorTOTP\Command; + +use OCA\TwoFactorTOTP\Db\TotpSecretMapper; +use OCP\DB\Exception; +use OCP\IDBConnection; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class CleanUp extends Command { + /** @var IDBConnection */ + private $db; + + /** @var IUserManager */ + private $userManager; + + /** @var TotpSecretMapper */ + private $totpSecretMapper; + + public function __construct( + IDBConnection $db, + IUserManager $userManager, + TotpSecretMapper $totpSecretMapper + ) { + parent::__construct(); + + $this->db = $db; + $this->userManager = $userManager; + $this->totpSecretMapper = $totpSecretMapper; + } + + protected function configure(): void { + $this + ->setName('twofactor_totp:cleanup') + ->setDescription('Remove orphaned totp secrets'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $io = new SymfonyStyle($input, $output); + $io->title('Remove totp secrets for deleted users'); + + foreach ($this->findUserIds() as $userId) { + if ($this->userManager->userExists($userId) === false) { + try { + $io->text('Delete secret for uid "' . $userId . '"'); + $this->totpSecretMapper->deleteSecretByUserId($userId); + } catch (Exception $e) { + $io->caution('Error deleting secret: ' . $e->getMessage()); + } + } + } + + $io->success('Orphaned totp secrets removed.'); + + $io->text('Thank you for using Two-Factor TOTP!'); + return 0; + } + + /** + * @throws Exception + */ + private function findUserIds(): array { + $userIds = []; + + $qb = $this->db->getQueryBuilder() + ->selectDistinct('user_id') + ->from($this->totpSecretMapper->getTableName()); + + $result = $qb->executeQuery(); + + while ($row = $result->fetch()) { + $userIds[] = $row['user_id']; + } + + $result->closeCursor(); + + return $userIds; + } +} diff --git a/lib/Db/TotpSecretMapper.php b/lib/Db/TotpSecretMapper.php index 50c605d..6428eee 100644 --- a/lib/Db/TotpSecretMapper.php +++ b/lib/Db/TotpSecretMapper.php @@ -26,6 +26,7 @@ namespace OCA\TwoFactorTOTP\Db; use Doctrine\DBAL\Statement; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; @@ -61,4 +62,16 @@ class TotpSecretMapper extends QBMapper { } return TotpSecret::fromRow($row); } + + /** + * @param string $uid + * @throws Exception + */ + public function deleteSecretByUserId(string $uid): void { + $qb = $this->db->getQueryBuilder(); + + $qb->delete($this->getTableName()) + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid))); + $qb->executeStatement(); + } } diff --git a/lib/Listener/UserDeleted.php b/lib/Listener/UserDeleted.php new file mode 100644 index 0000000..53631db --- /dev/null +++ b/lib/Listener/UserDeleted.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Daniel Kesselberg <mail@danielkesselberg.de> + * + * @author Daniel Kesselberg <mail@danielkesselberg.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/>. + * + */ + +namespace OCA\TwoFactorTOTP\Listener; + +use OCA\TwoFactorTOTP\Db\TotpSecretMapper; +use OCP\DB\Exception; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\User\Events\UserDeletedEvent; +use Psr\Log\LoggerInterface; + +class UserDeleted implements IEventListener { + + /** @var TotpSecretMapper */ + private $totpSecretMapper; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(TotpSecretMapper $totpSecretMapper, LoggerInterface $logger) { + $this->totpSecretMapper = $totpSecretMapper; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if ($event instanceof UserDeletedEvent) { + try { + $this->totpSecretMapper->deleteSecretByUserId($event->getUser()->getUID()); + } catch (Exception $e) { + $this->logger->warning($e->getMessage(), ['uid' => $event->getUser()->getUID()]); + } + } + } +} |