SetupService::finishSetup()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.4742

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 23
ccs 11
cts 15
cp 0.7332
rs 9.4555
c 0
b 0
f 0
cc 5
nc 5
nop 3
crap 5.4742
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * SPDX-FileCopyrightText: 2024 Christoph Wurst <[email protected]>
7
 * SPDX-License-Identifier: AGPL-3.0-or-later
8
 */
9
10
namespace OCA\TwoFactorGateway\Service;
11
12
use Exception;
13
use OCA\TwoFactorGateway\Exception\IdentifierMissingException;
14
use OCA\TwoFactorGateway\Exception\InvalidProviderException;
15
use OCA\TwoFactorGateway\Exception\MessageTransmissionException;
16
use OCA\TwoFactorGateway\Exception\VerificationException;
17
use OCA\TwoFactorGateway\Provider\Factory as ProviderFactory;
18
use OCA\TwoFactorGateway\Provider\Gateway\Factory as GatewayFactory;
19
use OCA\TwoFactorGateway\Provider\State;
20
use OCP\Authentication\TwoFactorAuth\IRegistry;
21
use OCP\IL10N;
22
use OCP\IUser;
23
use OCP\Security\ISecureRandom;
24
25
class SetupService {
26 5
	public function __construct(
27
		private StateStorage $stateStorage,
28
		private GatewayFactory $gatewayFactory,
29
		private ProviderFactory $providerFactory,
30
		private ISecureRandom $random,
31
		private IRegistry $providerRegistry,
32
		private IL10N $l10n,
33
	) {
34 5
	}
35
36
	public function getState(IUser $user, string $gatewayName): State {
37
		return $this->stateStorage->get($user, $gatewayName);
38
	}
39
40
	/**
41
	 * @throws IdentifierMissingException
42
	 */
43
	public function getChallengePhoneNumber(IUser $user, string $gatewayName): string {
44
		$state = $this->stateStorage->get($user, $gatewayName);
45
		$identifier = $state->getIdentifier();
46
		if (is_null($identifier)) {
47
			throw new IdentifierMissingException("verified identifier for $gatewayName is missing");
48
		}
49
50
		return $identifier;
51
	}
52
53
	/**
54
	 * Send out confirmation message and save current identifier in user settings
55
	 *
56
	 * @throws VerificationException
57
	 */
58 2
	public function startSetup(IUser $user, string $gatewayName, string $identifier): State {
59 2
		$verificationNumber = $this->random->generate(6, ISecureRandom::CHAR_DIGITS);
60 2
		$gateway = $this->gatewayFactory->get($gatewayName);
61
		try {
62 2
			$message = match ($gateway->getSettings()->allow_markdown ?? false) {
63
				true => $this->l10n->t('`%s` is your verification code.', [$verificationNumber]),
64 2
				default => $this->l10n->t('%s is your verification code.', [$verificationNumber]),
65 2
			};
66 2
			$gateway->send(
67 2
				$identifier,
68 2
				$message,
69 2
				['code' => $verificationNumber],
70 2
			);
71 1
		} catch (MessageTransmissionException $ex) {
72
			throw new VerificationException($ex->getMessage(), $ex->getCode(), $ex);
73
		}
74
75 1
		return $this->stateStorage->persist(
76 1
			State::verifying($user, $gatewayName, $identifier, $verificationNumber)
77 1
		);
78
	}
79
80 3
	public function finishSetup(IUser $user, string $gatewayName, string $token): State {
81 3
		$state = $this->stateStorage->get($user, $gatewayName);
82 3
		if (is_null($state->getVerificationCode())) {
83 1
			throw new VerificationException($this->l10n->t('no verification code set'));
84
		}
85
86 2
		if ($state->getVerificationCode() !== $token) {
87 1
			throw new VerificationException($this->l10n->t('verification token mismatch'));
88
		}
89
90
		try {
91 1
			$provider = $this->providerFactory->get($gatewayName);
92
		} catch (InvalidProviderException) {
93
			throw new VerificationException('Invalid provider');
94
		}
95 1
		$this->providerRegistry->enableProviderFor($provider, $user);
96
97
		try {
98 1
			return $this->stateStorage->persist(
99 1
				$state->verify()
100 1
			);
101
		} catch (Exception $e) {
102
			throw new VerificationException($e->getMessage());
103
		}
104
	}
105
106
	public function disable(IUser $user, string $gatewayName): State {
107
		try {
108
			$provider = $this->providerFactory->get($gatewayName);
109
		} catch (InvalidProviderException) {
110
			throw new VerificationException('Invalid provider');
111
		}
112
		$this->providerRegistry->enableProviderFor($provider, $user);
113
		$this->providerRegistry->disableProviderFor($provider, $user);
114
115
		try {
116
			return $this->stateStorage->persist(
117
				State::disabled($user, $gatewayName)
118
			);
119
		} catch (Exception $e) {
120
			throw new VerificationException($e->getMessage());
121
		}
122
	}
123
}
124