diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Controller/SettingsController.php | 18 | ||||
-rw-r--r-- | lib/Provider/AProvider.php | 11 | ||||
-rw-r--r-- | lib/Provider/State.php | 13 | ||||
-rw-r--r-- | lib/Service/Gateway/Factory.php | 64 | ||||
-rw-r--r-- | lib/Service/Gateway/IGateway.php | 8 | ||||
-rw-r--r-- | lib/Service/Gateway/SMS/Gateway.php | 10 | ||||
-rw-r--r-- | lib/Service/Gateway/Signal/Gateway.php | 10 | ||||
-rw-r--r-- | lib/Service/Gateway/Telegram/Gateway.php | 10 | ||||
-rw-r--r-- | lib/Service/Gateway/TestGateway.php | 10 | ||||
-rw-r--r-- | lib/Service/SetupService.php | 35 | ||||
-rw-r--r-- | lib/Service/StateStorage.php | 78 |
11 files changed, 153 insertions, 114 deletions
diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 94a5929..993db46 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -51,29 +51,31 @@ class SettingsController extends Controller { /** * @NoAdminRequired */ - public function getVerificationState(): JSONResponse { + public function getVerificationState(string $gateway): JSONResponse { $user = $this->userSession->getUser(); if (is_null($user)) { return new JSONResponse(null, Http::STATUS_BAD_REQUEST); } - return new JSONResponse($this->setup->getState($user)); + return new JSONResponse($this->setup->getState($user, $gateway)); } /** * @NoAdminRequired + * * @param string $identification + * * @return JSONResponse */ - public function startVerification(string $identifier): JSONResponse { + public function startVerification(string $gateway, string $identifier): JSONResponse { $user = $this->userSession->getUser(); if (is_null($user)) { return new JSONResponse(null, Http::STATUS_BAD_REQUEST); } - $state = $this->setup->startSetup($user, $identifier); + $state = $this->setup->startSetup($user, $gateway, $identifier); return new JSONResponse([ 'phoneNumber' => $state->getIdentifier(), @@ -83,7 +85,7 @@ class SettingsController extends Controller { /** * @NoAdminRequired */ - public function finishVerification(string $verificationCode): JSONResponse { + public function finishVerification(string $gateway, string $verificationCode): JSONResponse { $user = $this->userSession->getUser(); if (is_null($user)) { @@ -91,7 +93,7 @@ class SettingsController extends Controller { } try { - $this->setup->finishSetup($user, $verificationCode); + $this->setup->finishSetup($user, $gateway, $verificationCode); } catch (VerificationException $ex) { return new JSONResponse(null, Http::STATUS_BAD_REQUEST); } @@ -102,14 +104,14 @@ class SettingsController extends Controller { /** * @NoAdminRequired */ - public function revokeVerification(): JSONResponse { + public function revokeVerification(string $gateway): JSONResponse { $user = $this->userSession->getUser(); if (is_null($user)) { return new JSONResponse(null, Http::STATUS_BAD_REQUEST); } - return new JSONResponse($this->setup->disable($user)); + return new JSONResponse($this->setup->disable($user, $gateway)); } } diff --git a/lib/Provider/AProvider.php b/lib/Provider/AProvider.php index a69cb75..628e63e 100644 --- a/lib/Provider/AProvider.php +++ b/lib/Provider/AProvider.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace OCA\TwoFactorGateway\Provider; +use OCA\TwoFactorGateway\PhoneNumberMask; use OCA\TwoFactorGateway\Service\Gateway\IGateway; use OCA\TwoFactorGateway\Service\StateStorage; use OCP\Authentication\TwoFactorAuth\IProvider; @@ -40,7 +41,7 @@ abstract class AProvider implements IProvider { const STATE_ENABLED = 3; /** @var string */ - protected $gatewayId; + protected $gatewayName; /** @var IGateway */ protected $gateway; @@ -68,7 +69,7 @@ abstract class AProvider implements IProvider { ISecureRandom $secureRandom, IL10N $l10n) { $this->gateway = $gateway; - $this->gatewayId = $gatewayId; + $this->gatewayName = $gatewayId; $this->stateStorage = $stateStorage; $this->session = $session; $this->secureRandom = $secureRandom; @@ -79,7 +80,7 @@ abstract class AProvider implements IProvider { * Get unique identifier of this 2FA provider */ public function getId(): string { - return "gateway_$this->gatewayId"; + return "gateway_$this->gatewayName"; } private function getSecret(): string { @@ -100,7 +101,7 @@ abstract class AProvider implements IProvider { $secret = $this->getSecret(); try { - $identifier = $this->stateStorage->get($user)->getIdentifier(); + $identifier = $this->stateStorage->get($user, $this->gatewayName)->getIdentifier(); $this->gateway->send( $user, $identifier, @@ -135,7 +136,7 @@ abstract class AProvider implements IProvider { * Decides whether 2FA is enabled for the given user */ public function isTwoFactorAuthEnabledForUser(IUser $user): bool { - return $this->stateStorage->get($user)->getState() === self::STATE_ENABLED; + return $this->stateStorage->get($user, $this->gatewayName)->getState() === self::STATE_ENABLED; } } diff --git a/lib/Provider/State.php b/lib/Provider/State.php index 58023a4..2b4ae0b 100644 --- a/lib/Provider/State.php +++ b/lib/Provider/State.php @@ -36,7 +36,7 @@ class State implements JsonSerializable { /** @var int */ private $state; - /** @var string|null */ + /** @var string */ private $gatewayName; /** @var string|null */ @@ -47,7 +47,7 @@ class State implements JsonSerializable { public function __construct(IUser $user, int $state, - string $gatewayName = null, + string $gatewayName, string $identifier = null, string $verificationCode = null) { $this->user = $user; @@ -70,10 +70,11 @@ class State implements JsonSerializable { ); } - public static function disabled(IUser $user): State { + public static function disabled(IUser $user, string $gatewayName): State { return new State( $user, - SmsProvider::STATE_DISABLED + SmsProvider::STATE_DISABLED, + $gatewayName ); } @@ -102,9 +103,9 @@ class State implements JsonSerializable { } /** - * @return string|null + * @return string */ - public function getGatewayName() { + public function getGatewayName(): string { return $this->gatewayName; } diff --git a/lib/Service/Gateway/Factory.php b/lib/Service/Gateway/Factory.php new file mode 100644 index 0000000..a0dac01 --- /dev/null +++ b/lib/Service/Gateway/Factory.php @@ -0,0 +1,64 @@ +<?php + +declare(strict_types=1); + +/** + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * + * Nextcloud - Two-factor Gateway + * + * 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\TwoFactorGateway\Service\Gateway; + +use Exception; +use OCA\TwoFactorGateway\Service\Gateway\Signal\Gateway as SignalGateway; +use OCA\TwoFactorGateway\Service\Gateway\SMS\Gateway as SMSGateway; +use OCA\TwoFactorGateway\Service\Gateway\Telegram\Gateway as TelegramGateway; + +class Factory { + + /** @var SignalGateway */ + private $signalGateway; + + /** @var SMSGateway */ + private $smsGateway; + + /** @var TelegramGateway */ + private $telegramGateway; + + public function __construct(SignalGateway $signalGateway, + SMSGateway $smsGateway, + TelegramGateway $telegramGateway) { + + $this->signalGateway = $signalGateway; + $this->smsGateway = $smsGateway; + $this->telegramGateway = $telegramGateway; + } + + public function getGateway(string $name): IGateway { + switch ($name) { + case 'signal': + return $this->signalGateway; + case 'sms': + return $this->smsGateway; + case 'telegram': + return $this->telegramGateway; + default: + throw new Exception("Invalid gateway <$name>"); + } + } + +} diff --git a/lib/Service/Gateway/IGateway.php b/lib/Service/Gateway/IGateway.php index da467c5..5d0d881 100644 --- a/lib/Service/Gateway/IGateway.php +++ b/lib/Service/Gateway/IGateway.php @@ -29,14 +29,6 @@ use OCP\IUser; interface IGateway { /** - * Get a short description of this gateway's name so that users know how - * their messages are delivered, e.g. "Telegram" - * - * @return string - */ - public function getShortName(): string; - - /** * Get the gateway-specific configuration * * @return IGatewayConfig diff --git a/lib/Service/Gateway/SMS/Gateway.php b/lib/Service/Gateway/SMS/Gateway.php index 53f985d..e91456f 100644 --- a/lib/Service/Gateway/SMS/Gateway.php +++ b/lib/Service/Gateway/SMS/Gateway.php @@ -49,16 +49,6 @@ class Gateway implements IGateway { } /** - * Get a short description of this gateway's name so that users know how - * their messages are delivered, e.g. "Telegram" - * - * @return string - */ - public function getShortName(): string { - return 'SMS'; - } - - /** * Get the gateway-specific configuration * * @return GatewayConfig diff --git a/lib/Service/Gateway/Signal/Gateway.php b/lib/Service/Gateway/Signal/Gateway.php index c3c77f0..02d3f50 100644 --- a/lib/Service/Gateway/Signal/Gateway.php +++ b/lib/Service/Gateway/Signal/Gateway.php @@ -82,16 +82,6 @@ class Gateway implements IGateway { } /** - * Get a short description of this gateway's name so that users know how - * their messages are delivered, e.g. "Telegram" - * - * @return string - */ - public function getShortName(): string { - return 'Signal'; - } - - /** * Get the gateway-specific configuration * * @return IGatewayConfig diff --git a/lib/Service/Gateway/Telegram/Gateway.php b/lib/Service/Gateway/Telegram/Gateway.php index f8295ad..d8b355c 100644 --- a/lib/Service/Gateway/Telegram/Gateway.php +++ b/lib/Service/Gateway/Telegram/Gateway.php @@ -96,16 +96,6 @@ class Gateway implements IGateway { } /** - * Get a short description of this gateway's name so that users know how - * their messages are delivered, e.g. "Telegram" - * - * @return string - */ - public function getShortName(): string { - return 'Telegram'; - } - - /** * Get the gateway-specific configuration * * @return IGatewayConfig diff --git a/lib/Service/Gateway/TestGateway.php b/lib/Service/Gateway/TestGateway.php index 9cf7bb7..7d7f34c 100644 --- a/lib/Service/Gateway/TestGateway.php +++ b/lib/Service/Gateway/TestGateway.php @@ -42,16 +42,6 @@ class TestGateway implements IGateway { } /** - * Get a short description of this gateway's name so that users know how - * their messages are delivered, e.g. "Telegram" - * - * @return string - */ - public function getShortName(): string { - return 'Test'; - } - - /** * @return string */ public function getProviderDescription(): string { diff --git a/lib/Service/SetupService.php b/lib/Service/SetupService.php index 15f5945..a5c7d51 100644 --- a/lib/Service/SetupService.php +++ b/lib/Service/SetupService.php @@ -29,9 +29,9 @@ use OCA\TwoFactorGateway\Exception\IdentifierMissingException; use OCA\TwoFactorGateway\Exception\SmsTransmissionException; use OCA\TwoFactorGateway\Exception\VerificationException; use OCA\TwoFactorGateway\Exception\VerificationTransmissionException; +use OCA\TwoFactorGateway\Service\Gateway\Factory as GatewayFactory; use OCA\TwoFactorGateway\Provider\SmsProvider; use OCA\TwoFactorGateway\Provider\State; -use OCA\TwoFactorGateway\Service\Gateway\IGateway; use OCP\Authentication\TwoFactorAuth\IRegistry; use OCP\IUser; use OCP\Security\ISecureRandom; @@ -41,8 +41,8 @@ class SetupService { /** @var StateStorage */ private $stateStorage; - /** @var IGateway */ - private $smsService; + /** @var GatewayFactory */ + private $gatewayFactory; /** @var ISecureRandom */ private $random; @@ -54,29 +54,29 @@ class SetupService { private $providerRegistry; public function __construct(StateStorage $stateStorage, - IGateway $smsService, + GatewayFactory $gatewayFactory, ISecureRandom $random, SmsProvider $provider, IRegistry $providerRegistry) { $this->stateStorage = $stateStorage; - $this->smsService = $smsService; + $this->gatewayFactory = $gatewayFactory; $this->random = $random; $this->provider = $provider; $this->providerRegistry = $providerRegistry; } - public function getState(IUser $user): State { - return $this->stateStorage->get($user); + public function getState(IUser $user, string $gatewayName): State { + return $this->stateStorage->get($user, $gatewayName); } /** * @throws IdentifierMissingException */ - public function getChallengePhoneNumber(IUser $user): string { - $state = $this->stateStorage->get($user); + public function getChallengePhoneNumber(IUser $user, string $gatewayName): string { + $state = $this->stateStorage->get($user, $gatewayName); $identifier = $state->getIdentifier(); if (is_null($identifier)) { - throw new IdentifierMissingException('verified identifier is missing'); + throw new IdentifierMissingException("verified identifier for $gatewayName is missing"); } return $identifier; @@ -85,21 +85,22 @@ class SetupService { /** * Send out confirmation message and save current identifier in user settings */ - public function startSetup(IUser $user, string $identifier): State { + public function startSetup(IUser $user, string $gatewayName, string $identifier): State { $verificationNumber = $this->random->generate(6, ISecureRandom::CHAR_DIGITS); + $gateway = $this->gatewayFactory->getGateway($gatewayName); try { - $this->smsService->send($user, $identifier, "$verificationNumber is your Nextcloud verification code."); + $gateway->send($user, $identifier, "$verificationNumber is your Nextcloud verification code."); } catch (SmsTransmissionException $ex) { throw new VerificationTransmissionException('could not send verification code'); } return $this->stateStorage->persist( - State::verifying($user, $this->smsService->getShortName(), $identifier, $verificationNumber) + State::verifying($user, $gatewayName, $identifier, $verificationNumber) ); } - public function finishSetup(IUser $user, string $token): State { - $state = $this->stateStorage->get($user); + public function finishSetup(IUser $user, string $gatewayName, string $token): State { + $state = $this->stateStorage->get($user, $gatewayName); if (is_null($state->getVerificationCode())) { throw new Exception('no verification code set'); } @@ -115,11 +116,11 @@ class SetupService { ); } - public function disable(IUser $user): State { + public function disable(IUser $user, string $gatewayName): State { $this->providerRegistry->disableProviderFor($this->provider, $user); return $this->stateStorage->persist( - State::disabled($user) + State::disabled($user, $gatewayName) ); } diff --git a/lib/Service/StateStorage.php b/lib/Service/StateStorage.php index 75e3f0f..10307d5 100644 --- a/lib/Service/StateStorage.php +++ b/lib/Service/StateStorage.php @@ -28,7 +28,6 @@ use Exception; use OCA\TwoFactorGateway\AppInfo\Application; use OCA\TwoFactorGateway\Provider\SmsProvider; use OCA\TwoFactorGateway\Provider\State; -use OCA\TwoFactorGateway\Service\Gateway\IGateway; use OCP\IConfig; use OCP\IUser; @@ -41,10 +40,29 @@ class StateStorage { $this->config = $config; } - public function get(IUser $user): State { - $isVerified = $this->config->getUserValue($user->getUID(), Application::APP_NAME, 'verified', 'false') === 'true'; - $identifier = $this->config->getUserValue($user->getUID(), 'twofactor_gateway', 'identifier', null); - $verificationCode = $this->config->getUserValue($user->getUID(), 'twofactor_gateway', 'verification_code', null); + private function buildConfigKey(string $gatewayName, string $key) { + return "twofactor_gateway_$gatewayName" . "_$key"; + } + + private function getUserValue(IUser $user, string $gatewayName, string $key, $default = '') { + $gatewayKey = $this->buildConfigKey($gatewayName, $key); + return $this->config->getUserValue($user->getUID(), Application::APP_NAME, $gatewayKey, $default); + } + + private function setUserValue(IUser $user, string $gatewayName, string $key, $value) { + $gatewayKey = $this->buildConfigKey($gatewayName, $key); + $this->config->setUserValue($user->getUID(), Application::APP_NAME, $gatewayKey, $value); + } + + private function deleteUserValue(IUser $user, string $gatewayName, string $key) { + $gatewayKey = $this->buildConfigKey($gatewayName, $key); + $this->config->deleteUserValue($user->getUID(), Application::APP_NAME, $gatewayKey); + } + + public function get(IUser $user, string $gatewayName): State { + $isVerified = $this->getUserValue($user, $gatewayName, 'verified', 'false') === 'true'; + $identifier = $this->getUserValue($user, $gatewayName, 'identifier', null); + $verificationCode = $this->getUserValue($user, $gatewayName, 'verification_code', null); if ($isVerified) { $state = SmsProvider::STATE_ENABLED; @@ -57,7 +75,7 @@ class StateStorage { return new State( $user, $state, - '', // TODO: fix + $gatewayName, $identifier, $verificationCode ); @@ -66,55 +84,55 @@ class StateStorage { public function persist(State $state): State { switch ($state->getState()) { case SmsProvider::STATE_DISABLED: - $this->config->deleteUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->deleteUserValue( + $state->getUser(), + $state->getGatewayName(), 'verified' ); - $this->config->deleteUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->deleteUserValue( + $state->getUser(), + $state->getGatewayName(), 'verification_code' ); break; case SmsProvider::STATE_VERIFYING: - $this->config->setUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->setUserValue( + $state->getUser(), + $state->getGatewayName(), 'identifier', $state->getIdentifier() ); - $this->config->setUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->setUserValue( + $state->getUser(), + $state->getGatewayName(), 'verification_code', $state->getVerificationCode() ); - $this->config->setUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->setUserValue( + $state->getUser(), + $state->getGatewayName(), 'verified', 'false' ); break; case SmsProvider::STATE_ENABLED: - $this->config->setUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->setUserValue( + $state->getUser(), + $state->getGatewayName(), 'identifier', $state->getIdentifier() ); - $this->config->setUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->setUserValue( + $state->getUser(), + $state->getGatewayName(), 'verification_code', $state->getVerificationCode() ); - $this->config->setUserValue( - $state->getUser()->getUID(), - Application::APP_NAME, + $this->setUserValue( + $state->getUser(), + $state->getGatewayName(), 'verified', 'true' ); |