Completed
Pull Request — master (#92)
by Christoph
03:15
created

AProvider::getTemplate()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 19
rs 9.8333
c 0
b 0
f 0
cc 2
nc 3
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\Exception\SmsTransmissionException;
27
use OCA\TwoFactorGateway\PhoneNumberMask;
28
use OCA\TwoFactorGateway\Service\Gateway\IGateway;
29
use OCA\TwoFactorGateway\Service\StateStorage;
30
use OCP\Authentication\TwoFactorAuth\IProvider;
31
use OCP\IL10N;
32
use OCP\ISession;
33
use OCP\IUser;
34
use OCP\Security\ISecureRandom;
35
use OCP\Template;
36
37
abstract class AProvider implements IProvider {
38
39
	const STATE_DISABLED = 0;
40
	const STATE_START_VERIFICATION = 1;
41
	const STATE_VERIFYING = 2;
42
	const STATE_ENABLED = 3;
43
44
	/** @var string */
45
	protected $gatewayName;
46
47
	/** @var IGateway */
48
	protected $gateway;
49
50
	/** @var StateStorage */
51
	protected $stateStorage;
52
53
	/** @var ISession */
54
	protected $session;
55
56
	/** @var ISecureRandom */
57
	protected $secureRandom;
58
59
	/** @var IL10N */
60
	protected $l10n;
61
62
	private function getSessionKey() {
63
		return "twofactor_gateway_$this->gatewayId" . "_secret";
0 ignored issues
show
Bug introduced by
The property gatewayId does not exist on OCA\TwoFactorGateway\Provider\AProvider. Did you mean gateway?
Loading history...
64
	}
65
66
	public function __construct(string $gatewayId,
67
								IGateway $gateway,
68
								StateStorage $stateStorage,
69
								ISession $session,
70
								ISecureRandom $secureRandom,
71
								IL10N $l10n) {
72
		$this->gateway = $gateway;
73
		$this->gatewayName = $gatewayId;
74
		$this->stateStorage = $stateStorage;
75
		$this->session = $session;
76
		$this->secureRandom = $secureRandom;
77
		$this->l10n = $l10n;
78
	}
79
80
	/**
81
	 * Get unique identifier of this 2FA provider
82
	 */
83
	public function getId(): string {
84
		return "gateway_$this->gatewayName";
85
	}
86
87
	private function getSecret(): string {
88
		if ($this->session->exists($this->getSessionKey())) {
89
			return $this->session->get($this->getSessionKey());
90
		}
91
92
		$secret = $this->secureRandom->generate(6, ISecureRandom::CHAR_DIGITS);
93
		$this->session->set($this->getSessionKey(), $secret);
94
95
		return $secret;
96
	}
97
98
	/**
99
	 * Get the template for rending the 2FA provider view
100
	 */
101
	public function getTemplate(IUser $user): Template {
102
		$secret = $this->getSecret();
103
104
		try {
105
			$identifier = $this->stateStorage->get($user, $this->gatewayName)->getIdentifier();
106
			$this->gateway->send(
107
				$user,
108
				$identifier,
109
				$this->l10n->t('%s is your Nextcloud authentication code', [
110
					$secret
111
				])
112
			);
113
		} catch (SmsTransmissionException $ex) {
114
			return new Template('twofactor_gateway', 'error');
115
		}
116
117
		$tmpl = new Template('twofactor_gateway', 'challenge');
118
		$tmpl->assign('phone', PhoneNumberMask::maskNumber($identifier));
119
		return $tmpl;
120
	}
121
122
	/**
123
	 * Verify the given challenge
124
	 */
125
	public function verifyChallenge(IUser $user, string $challenge): bool {
126
		$valid = $this->session->exists($this->getSessionKey())
127
			&& $this->session->get($this->getSessionKey()) === $challenge;
128
129
		if ($valid) {
130
			$this->session->remove($this->getSessionKey());
131
		}
132
133
		return $valid;
134
	}
135
136
	/**
137
	 * Decides whether 2FA is enabled for the given user
138
	 */
139
	public function isTwoFactorAuthEnabledForUser(IUser $user): bool {
140
		return $this->stateStorage->get($user, $this->gatewayName)->getState() === self::STATE_ENABLED;
141
	}
142
143
}
144