diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2018-12-03 12:31:07 +0300 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2018-12-04 10:34:24 +0300 |
commit | 3ac1d325ecde5aab769435aa5e0a8ef6ae98956f (patch) | |
tree | ef7f913d84fe2329951c2db810742f9ba1d9e4e6 | |
parent | 97b77e57efb0c41d1143f044c3fbb4f50afff5a0 (diff) |
Make the provider deactivatable
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
-rw-r--r-- | lib/Activity/Provider.php | 3 | ||||
-rw-r--r-- | lib/AppInfo/Application.php | 11 | ||||
-rw-r--r-- | lib/Event/DisabledByAdmin.php | 35 | ||||
-rw-r--r-- | lib/Listener/StateChangeActivity.php | 11 | ||||
-rw-r--r-- | lib/Provider/TotpProvider.php | 13 | ||||
-rw-r--r-- | lib/Service/ITotp.php | 2 | ||||
-rw-r--r-- | lib/Service/Totp.php | 9 | ||||
-rw-r--r-- | tests/Unit/Activity/ProviderTest.php | 1 | ||||
-rw-r--r-- | tests/Unit/Listener/StateChangeActivityTest.php | 37 | ||||
-rw-r--r-- | tests/Unit/Provider/TotpProviderTest.php | 9 |
10 files changed, 124 insertions, 7 deletions
diff --git a/lib/Activity/Provider.php b/lib/Activity/Provider.php index e8d62fc..922ee64 100644 --- a/lib/Activity/Provider.php +++ b/lib/Activity/Provider.php @@ -57,6 +57,9 @@ class Provider implements IProvider { case 'totp_disabled_subject': $event->setSubject($l->t('You disabled TOTP two-factor authentication for your account')); break; + case 'totp_disabled_by_admin': + $event->setSubject($l->t('TOTP two-factor authentication disabled by an admin')); + break; } return $event; } diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 21fb05d..6b8536d 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace OCA\TwoFactorTOTP\AppInfo; +use OCA\TwoFactorTOTP\Event\DisabledByAdmin; use OCA\TwoFactorTOTP\Event\StateChanged; use OCA\TwoFactorTOTP\Listener\IListener; use OCA\TwoFactorTOTP\Listener\StateChangeActivity; @@ -51,6 +52,16 @@ class Application extends App { $listener->handle($event); } }); + $dispatcher->addListener(DisabledByAdmin::class, function (DisabledByAdmin $event) use ($container) { + /** @var IListener[] $listeners */ + $listeners = [ + $container->query(StateChangeActivity::class), + ]; + + foreach ($listeners as $listener) { + $listener->handle($event); + } + }); } } diff --git a/lib/Event/DisabledByAdmin.php b/lib/Event/DisabledByAdmin.php new file mode 100644 index 0000000..6f354ac --- /dev/null +++ b/lib/Event/DisabledByAdmin.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @copyright Copyright (c) 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * Two-factor TOTP + * + * 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\Event; + +use OCP\IUser; + +class DisabledByAdmin extends StateChanged { + + public function __construct(IUser $user) { + parent::__construct($user, false); + } + +} diff --git a/lib/Listener/StateChangeActivity.php b/lib/Listener/StateChangeActivity.php index d60af3f..929b50f 100644 --- a/lib/Listener/StateChangeActivity.php +++ b/lib/Listener/StateChangeActivity.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace OCA\TwoFactorTOTP\Listener; +use OCA\TwoFactorTOTP\Event\DisabledByAdmin; use OCA\TwoFactorTOTP\Event\StateChanged; use OCP\Activity\IManager as ActivityManager; use Symfony\Component\EventDispatcher\Event; @@ -39,15 +40,19 @@ class StateChangeActivity implements IListener { public function handle(Event $event) { if ($event instanceof StateChanged) { + if ($event instanceof DisabledByAdmin) { + $subject = 'totp_disabled_by_admin'; + } else { + $subject = $event->isEnabled() ? 'totp_enabled_subject' : 'totp_disabled_subject'; + } $user = $event->getUser(); - $subject = $event->isEnabled() ? 'totp_enabled_subject' : 'totp_disabled_subject'; $activity = $this->activityManager->generateEvent(); $activity->setApp('twofactor_totp') ->setType('security') ->setAuthor($user->getUID()) - ->setAffectedUser($user->getUID()); - $activity->setSubject($subject); + ->setAffectedUser($user->getUID()) + ->setSubject($subject); $this->activityManager->publish($activity); } } diff --git a/lib/Provider/TotpProvider.php b/lib/Provider/TotpProvider.php index 1e1b99d..94096c2 100644 --- a/lib/Provider/TotpProvider.php +++ b/lib/Provider/TotpProvider.php @@ -25,6 +25,7 @@ namespace OCA\TwoFactorTOTP\Provider; use OCA\TwoFactorTOTP\Service\ITotp; use OCA\TwoFactorTOTP\Settings\Personal; +use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin; use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IProvidesIcons; @@ -33,7 +34,7 @@ use OCP\IL10N; use OCP\IUser; use OCP\Template; -class TotpProvider implements IProvider, IProvidesIcons, IProvidesPersonalSettings { +class TotpProvider implements IProvider, IProvidesIcons, IProvidesPersonalSettings, IDeactivatableByAdmin { /** @var ITotp */ private $totp; @@ -101,4 +102,14 @@ class TotpProvider implements IProvider, IProvidesIcons, IProvidesPersonalSettin public function getPersonalSettings(IUser $user): IPersonalProviderSettings { return new Personal($this->totp->hasSecret($user) ? ITotp::STATE_ENABLED : ITotp::STATE_DISABLED); } + + /** + * Disable this provider for the given user. + * + * @param IUser $user the user to deactivate this provider for + */ + public function disableFor(IUser $user) { + $this->totp->deleteSecret($user, true); + } + } diff --git a/lib/Service/ITotp.php b/lib/Service/ITotp.php index 1864ec2..d27bef2 100644 --- a/lib/Service/ITotp.php +++ b/lib/Service/ITotp.php @@ -63,7 +63,7 @@ interface ITotp { /** * @param IUser $user */ - public function deleteSecret(IUser $user); + public function deleteSecret(IUser $user, bool $byAdmin = false); /** * @param IUser $user diff --git a/lib/Service/Totp.php b/lib/Service/Totp.php index f40716c..678797b 100644 --- a/lib/Service/Totp.php +++ b/lib/Service/Totp.php @@ -27,6 +27,7 @@ namespace OCA\TwoFactorTOTP\Service; use Base32\Base32; use OCA\TwoFactorTOTP\Db\TotpSecret; use OCA\TwoFactorTOTP\Db\TotpSecretMapper; +use OCA\TwoFactorTOTP\Event\DisabledByAdmin; use OCA\TwoFactorTOTP\Event\StateChanged; use OCA\TwoFactorTOTP\Exception\NoTotpSecretFoundException; use OCP\AppFramework\Db\DoesNotExistException; @@ -101,7 +102,7 @@ class Totp implements ITotp { return true; } - public function deleteSecret(IUser $user) { + public function deleteSecret(IUser $user, bool $byAdmin = false) { try { // TODO: execute DELETE sql in mapper instead $dbSecret = $this->secretMapper->getSecret($user); @@ -110,7 +111,11 @@ class Totp implements ITotp { // Ignore } - $this->eventDispatcher->dispatch(StateChanged::class, new StateChanged($user, false)); + if ($byAdmin) { + $this->eventDispatcher->dispatch(DisabledByAdmin::class, new DisabledByAdmin($user)); + } else { + $this->eventDispatcher->dispatch(StateChanged::class, new StateChanged($user, false)); + } } public function validateSecret(IUser $user, $key): bool { diff --git a/tests/Unit/Activity/ProviderTest.php b/tests/Unit/Activity/ProviderTest.php index 5678fcc..d798fcf 100644 --- a/tests/Unit/Activity/ProviderTest.php +++ b/tests/Unit/Activity/ProviderTest.php @@ -65,6 +65,7 @@ class ProviderTest extends TestCase { return [ ['totp_enabled_subject'], ['totp_disabled_subject'], + ['totp_disabled_by_admin'], ]; } diff --git a/tests/Unit/Listener/StateChangeActivityTest.php b/tests/Unit/Listener/StateChangeActivityTest.php index 8b3e05e..540f864 100644 --- a/tests/Unit/Listener/StateChangeActivityTest.php +++ b/tests/Unit/Listener/StateChangeActivityTest.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace OCA\TwoFactorTOTP\Unit\Listener; use ChristophWurst\Nextcloud\Testing\TestCase; +use OCA\TwoFactorTOTP\Event\DisabledByAdmin; use OCA\TwoFactorTOTP\Event\StateChanged; use OCA\TwoFactorTOTP\Listener\StateChangeActivity; use OCP\Activity\IEvent; @@ -79,4 +80,40 @@ class StateChangeActivityTest extends TestCase { $this->listener->handle($event); } + public function testHandleDisabledByAdminEvent() { + $uid = 'user234'; + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn($uid); + $event = new DisabledByAdmin($user); + $activityEvent = $this->createMock(IEvent::class); + $this->activityManager->expects($this->once()) + ->method('generateEvent') + ->willReturn($activityEvent); + $activityEvent->expects($this->once()) + ->method('setApp') + ->with('twofactor_totp') + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setType') + ->with('security') + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setAuthor') + ->with($uid) + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setAffectedUser') + ->with($uid) + ->willReturnSelf(); + $activityEvent->expects($this->once()) + ->method('setSubject') + ->with('totp_disabled_by_admin') + ->willReturnSelf(); + $this->activityManager->expects($this->once()) + ->method('publish') + ->with($activityEvent); + + $this->listener->handle($event); + } + }
\ No newline at end of file diff --git a/tests/Unit/Provider/TotpProviderTest.php b/tests/Unit/Provider/TotpProviderTest.php index 0f69343..2e8376e 100644 --- a/tests/Unit/Provider/TotpProviderTest.php +++ b/tests/Unit/Provider/TotpProviderTest.php @@ -113,4 +113,13 @@ class TotpProviderTest extends TestCase { $this->assertEquals($expected, $actual); } + public function testDeactivate() { + $user = $this->createMock(IUser::class); + $this->totp->expects($this->once()) + ->method('deleteSecret') + ->with($user); + + $this->provider->disableFor($user); + } + } |