Passed
Push — developer ( 5288e6...1516b4 )
by Mariusz
25:46 queued 07:18
created

Users_Totp_Authmethod::createQrCode()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 15
rs 9.8666
ccs 0
cts 12
cp 0
cc 4
nc 4
nop 2
crap 20
1
<?php
2
3
/**
4
 * TOTP authentication method file.
5
 * TOTP - Time-based One-time Password.
6
 *
7
 * @package AuthMethod
8
 *
9
 * @copyright YetiForce S.A.
10
 * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
11
 * @author    Arkadiusz Adach <[email protected]>
12
 * @author    Mariusz Krzaczkowski <[email protected]>
13
 */
14
15
/**
16
 * TOTP authentication method class.
17
 */
18
class Users_Totp_Authmethod
19
{
20
	/**
21
	 * @var string[] User authentication mode possible values.
22
	 *               TOTP_OFF - 2FA TOTP is checking off
23
	 *               TOTP_OPTIONAL - It is defined by the user
24
	 *               TOTP_OBLIGATORY - It is obligatory.
25
	 */
26
	const ALLOWED_USER_AUTHY_MODE = ['TOTP_OFF', 'TOTP_OPTIONAL', 'TOTP_OBLIGATORY'];
27
28
	/** @var int - User id */
29
	private $userId;
30
31
	/** @var \PragmaRX\Google2FA\Google2FA */
32
	private $authenticator;
33
34
	/** @var string - Secret code */
35
	private $secret;
36
37
	/**
38
	 * Constructor.
39
	 *
40
	 * @param int $userId - Id of user
41
	 */
42
	public function __construct(int $userId)
43
	{
44
		$this->userId = $userId;
45
		$this->authenticator = new \PragmaRX\Google2FA\Google2FA();
46
	}
47
48
	/**
49
	 * Generate TOTP code url.
50
	 *
51
	 * @param string      $secret - REQUIRED: The secret parameter is an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted.
52
	 * @param string      $name   - The name is used to identify which account a key is associated with.
53
	 * @param string|null $issuer - The issuer parameter is a string value indicating the provider or service this account is associated with, URL-encoded according to RFC 3986.
54
	 *
55
	 * @return string - Url
56
	 */
57
	public function getOtpAuthUrl($secret, $name, $issuer = null)
58
	{
59
		if (null === $issuer) {
60
			$issuer = parse_url(App\Config::main('site_URL'))['host'] ?? '';
61
		}
62
		return $this->authenticator->getQRCodeUrl($issuer, $name, $secret);
63
	}
64
65
	/**
66
	 * Creating a secret code for TOTP.
67
	 *
68
	 * @return string
69
	 */
70
	public function createSecret()
71
	{
72
		return $this->secret = $this->authenticator->generateSecretKey();
73
	}
74
75
	/**
76
	 * Create QR code.
77
	 *
78
	 * @param string $otpAuthUrl
79
	 * @param string $type       - acceptable types [HTML, SVG, PNG]
80
	 *
81
	 * @throws \App\Exceptions\NotAllowedMethod
82
	 *
83
	 * @return \Milon\Barcode\path|string - HTML code
0 ignored issues
show
Bug introduced by
The type Milon\Barcode\path was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
84
	 */
85
	public function createQrCode($otpAuthUrl, $type = 'HTML')
86
	{
87
		$qrCodeGenerator = new \Milon\Barcode\DNS2D();
88
		$qrCodeGenerator->setStorPath(__DIR__ . App\Config::main('tmp_dir'));
89
		switch ($type) {
90
			case 'HTML':
91
				return $qrCodeGenerator->getBarcodeHTML($otpAuthUrl, 'QRCODE');
92
			case 'SVG':
93
				return $qrCodeGenerator->getBarcodeSVG($otpAuthUrl, 'QRCODE');
94
			case 'PNG':
95
				return '<img src="data:image/png;base64,' . $qrCodeGenerator->getBarcodePNG($otpAuthUrl, 'QRCODE', 10, 10) . '" alt="QR code" class="col-auto p-0" />';
96
			default:
97
				break;
98
		}
99
		throw new \App\Exceptions\NotAllowedMethod('LBL_NOT_EXIST: ' . $type);
100
	}
101
102
	/**
103
	 * Create URL code for user.
104
	 *
105
	 * @throws \App\Exceptions\NotAllowedMethod
106
	 *
107
	 * @return \Milon\Barcode\path|string
108
	 */
109
	public function createUrl()
110
	{
111
		return $this->getOtpAuthUrl($this->secret, \App\User::getUserModel($this->userId)->getDetail('user_name'));
112
	}
113
114
	/**
115
	 * 2FA - verification of the code from the user.
116
	 *
117
	 * @param string $secret
118
	 * @param string $userCode
119
	 *
120
	 * @return bool
121
	 */
122
	public function verifyCode($secret, $userCode)
123
	{
124
		return $this->authenticator->verifyKey($secret, (string) $userCode);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->authentica...ret, (string)$userCode) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
125
	}
126 1
127
	/**
128 1
	 * 2FA - get the current one time password.
129
	 *
130
	 * @return string
131
	 */
132
	public function getCode(): string
133
	{
134
		return $this->authenticator->getCurrentOtp($this->secret);
135
	}
136
137
	/**
138 1
	 * Determine whether 2FA is required.
139
	 *
140 1
	 * @param int|null $userId - if null then getCurrentUserRealId
141 1
	 *
142
	 * @return bool
143 1
	 */
144 1
	public static function isActive($userId = null)
145
	{
146 1
		$isActive = false;
147 1
		if (empty($userId)) {
148 1
			$userId = \App\User::getCurrentUserRealId();
149
		}
150
		$userModel = \App\User::getUserModel($userId);
151
		if ('PLL_PASSWORD_2FA' === $userModel->getDetail('login_method') || 'PLL_LDAP_2FA' === $userModel->getDetail('login_method')) {
152
			switch (App\Config::security('USER_AUTHY_MODE')) {
153
				case 'TOTP_OPTIONAL':
154
					$isActive = 'PLL_AUTHY_TOTP' === $userModel->getDetail('authy_methods');
155
					break;
156
				case 'TOTP_OBLIGATORY':
157
					$isActive = true;
158
					break;
159
				default:
160
					break;
161
			}
162
		}
163
		return $isActive;
164
	}
165
166
	/**
167
	 * Check if 2FA initiation is necessary.
168
	 *
169
	 * @param int|null $userId - if null then getCurrentUserRealId
170
	 *
171
	 * @return bool
172
	 */
173
	public static function mustInit($userId = null)
174
	{
175
		if (empty($userId)) {
176
			$userId = \App\User::getCurrentUserRealId();
177
		}
178
		return empty(\App\User::getUserModel($userId)->getDetail('authy_secret_totp'));
179
	}
180
}
181