Completed
Push — master ( 844902...2e951f )
by Vitor
15s queued 13s
created

AProvider::disableFor()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @author Christoph Wurst <[email protected]>
7
 *
8
 * Nextcloud - Two-factor Gateway
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\TwoFactorGateway\Provider;
25
26
use OCA\TwoFactorGateway\AppInfo\Application;
27
use OCA\TwoFactorGateway\Exception\SmsTransmissionException;
28
use OCA\TwoFactorGateway\PhoneNumberMask;
29
use OCA\TwoFactorGateway\Service\Gateway\IGateway;
30
use OCA\TwoFactorGateway\Service\StateStorage;
31
use OCA\TwoFactorGateway\Settings\PersonalSettings;
32
use OCP\Authentication\TwoFactorAuth\IDeactivatableByAdmin;
33
use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings;
34
use OCP\Authentication\TwoFactorAuth\IProvider;
35
use OCP\Authentication\TwoFactorAuth\IProvidesIcons;
36
use OCP\Authentication\TwoFactorAuth\IProvidesPersonalSettings;
37
use OCP\IL10N;
38
use OCP\ISession;
39
use OCP\IUser;
40
use OCP\Security\ISecureRandom;
41
use OCP\Template;
42
43
abstract class AProvider implements IProvider, IProvidesIcons, IDeactivatableByAdmin, IProvidesPersonalSettings {
44
	public const STATE_DISABLED = 0;
45
	public const STATE_START_VERIFICATION = 1;
46
	public const STATE_VERIFYING = 2;
47
	public const STATE_ENABLED = 3;
48
49
	/** @var string */
50
	protected $gatewayName;
51
52
	/** @var IGateway */
53
	protected $gateway;
54
55
	/** @var StateStorage */
56
	protected $stateStorage;
57
58
	/** @var ISession */
59
	protected $session;
60
61
	/** @var ISecureRandom */
62
	protected $secureRandom;
63
64
	/** @var IL10N */
65
	protected $l10n;
66
67
	private function getSessionKey() {
68
		return "twofactor_gateway_" . $this->gatewayName . "_secret";
69
	}
70
71
	public function __construct(string $gatewayId,
72
								IGateway $gateway,
73
								StateStorage $stateStorage,
74
								ISession $session,
75
								ISecureRandom $secureRandom,
76
								IL10N $l10n) {
77
		$this->gateway = $gateway;
78
		$this->gatewayName = $gatewayId;
79
		$this->stateStorage = $stateStorage;
80
		$this->session = $session;
81
		$this->secureRandom = $secureRandom;
82
		$this->l10n = $l10n;
83
	}
84
85
	/**
86
	 * Get unique identifier of this 2FA provider
87
	 */
88
	public function getId(): string {
89
		return "gateway_$this->gatewayName";
90
	}
91
92
	private function getSecret(): string {
93
		if ($this->session->exists($this->getSessionKey())) {
94
			return $this->session->get($this->getSessionKey());
95
		}
96
97
		$secret = $this->secureRandom->generate(6, ISecureRandom::CHAR_DIGITS);
98
		$this->session->set($this->getSessionKey(), $secret);
99
100
		return $secret;
101
	}
102
103
	/**
104
	 * Get the template for rending the 2FA provider view
105
	 */
106
	public function getTemplate(IUser $user): Template {
107
		$secret = $this->getSecret();
108
109
		try {
110
			$identifier = $this->stateStorage->get($user, $this->gatewayName)->getIdentifier();
111
			$this->gateway->send(
112
				$user,
113
				$identifier,
0 ignored issues
show
Bug introduced by
It seems like $identifier can also be of type null; however, parameter $identifier of OCA\TwoFactorGateway\Ser...ateway\IGateway::send() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
				/** @scrutinizer ignore-type */ $identifier,
Loading history...
114
				$this->l10n->t('%s is your Nextcloud authentication code', [
115
					$secret
116
				])
117
			);
118
		} catch (SmsTransmissionException $ex) {
119
			return new Template('twofactor_gateway', 'error');
120
		}
121
122
		$tmpl = new Template('twofactor_gateway', 'challenge');
123
		$tmpl->assign('phone', PhoneNumberMask::maskNumber($identifier));
0 ignored issues
show
Bug introduced by
It seems like $identifier can also be of type null; however, parameter $number of OCA\TwoFactorGateway\PhoneNumberMask::maskNumber() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
		$tmpl->assign('phone', PhoneNumberMask::maskNumber(/** @scrutinizer ignore-type */ $identifier));
Loading history...
124
		return $tmpl;
125
	}
126
127
	/**
128
	 * Verify the given challenge
129
	 */
130
	public function verifyChallenge(IUser $user, string $challenge): bool {
131
		$valid = $this->session->exists($this->getSessionKey())
132
			&& $this->session->get($this->getSessionKey()) === $challenge;
133
134
		if ($valid) {
135
			$this->session->remove($this->getSessionKey());
136
		}
137
138
		return $valid;
139
	}
140
141
	/**
142
	 * Decides whether 2FA is enabled for the given user
143
	 */
144
	public function isTwoFactorAuthEnabledForUser(IUser $user): bool {
145
		return $this->stateStorage->get($user, $this->gatewayName)->getState() === self::STATE_ENABLED;
146
	}
147
148
	public function getPersonalSettings(IUser $user): IPersonalProviderSettings {
149
		return new PersonalSettings($this->gatewayName);
150
	}
151
152
	public function getLightIcon(): String {
153
		return image_path(Application::APP_ID, 'app.svg');
0 ignored issues
show
Bug introduced by
The function image_path was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
		return /** @scrutinizer ignore-call */ image_path(Application::APP_ID, 'app.svg');
Loading history...
154
	}
155
156
	public function getDarkIcon(): String {
157
		return image_path(Application::APP_ID, 'app-dark.svg');
0 ignored issues
show
Bug introduced by
The function image_path was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

157
		return /** @scrutinizer ignore-call */ image_path(Application::APP_ID, 'app-dark.svg');
Loading history...
158
	}
159
160
	public function disableFor(IUser $user) {
161
		$state = $this->stateStorage->get($user, $this->gatewayName);
162
		if ($state->getState() === self::STATE_ENABLED) {
163
			$this->stateStorage->persist($state->disabled($user, $this->gatewayName));
164
		}
165
	}
166
}
167