|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* |
|
4
|
|
|
* 2FA extension for the phpBB Forum Software package. |
|
5
|
|
|
* |
|
6
|
|
|
* @copyright (c) 2015 Paul Sohier |
|
7
|
|
|
* @license GNU General Public License, version 2 (GPL-2.0) |
|
8
|
|
|
* |
|
9
|
|
|
*/ |
|
10
|
|
|
|
|
11
|
|
|
namespace paul999\tfa\modules; |
|
12
|
|
|
|
|
13
|
|
|
use OTPAuthenticate\OTPAuthenticate; |
|
14
|
|
|
use OTPAuthenticate\OTPHelper; |
|
15
|
|
|
use phpbb\db\driver\driver_interface; |
|
16
|
|
|
use phpbb\request\request_interface; |
|
17
|
|
|
use phpbb\template\template; |
|
18
|
|
|
use phpbb\user; |
|
19
|
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
|
20
|
|
|
|
|
21
|
|
|
class otp extends abstract_module |
|
22
|
|
|
{ |
|
23
|
|
|
/** |
|
24
|
|
|
* @var \OTPAuthenticate\OTPHelper |
|
25
|
|
|
*/ |
|
26
|
|
|
private $otp_helper; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* @var \OTPAuthenticate\OTPAuthenticate |
|
30
|
|
|
*/ |
|
31
|
|
|
private $otp; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* @var \phpbb\db\driver\driver_interface |
|
35
|
|
|
*/ |
|
36
|
|
|
private $db; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* @var \phpbb\user |
|
40
|
|
|
*/ |
|
41
|
|
|
private $user; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* @var \phpbb\request\request_interface |
|
45
|
|
|
*/ |
|
46
|
|
|
private $request; |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* @var \phpbb\template\template |
|
50
|
|
|
*/ |
|
51
|
|
|
private $template; |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* @var string |
|
55
|
|
|
*/ |
|
56
|
|
|
private $otp_registration_table; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* OTP constructor. |
|
60
|
|
|
* |
|
61
|
|
|
* @param \phpbb\db\driver\driver_interface $db |
|
62
|
|
|
* @param \phpbb\user $user |
|
63
|
|
|
* @param \phpbb\request\request_interface $request |
|
64
|
|
|
* @param \phpbb\template\template $template |
|
65
|
|
|
* @param string $otp_registration_table |
|
66
|
|
|
*/ |
|
67
|
|
|
public function __construct(driver_interface $db, user $user, request_interface $request, template $template, $otp_registration_table) |
|
68
|
|
|
{ |
|
69
|
|
|
$this->otp_helper = new OTPHelper(); |
|
70
|
|
|
$this->otp = new OTPAuthenticate(); |
|
71
|
|
|
$this->db = $db; |
|
72
|
|
|
$this->user = $user; |
|
73
|
|
|
$this->request = $request; |
|
74
|
|
|
$this->template = $template; |
|
75
|
|
|
$this->otp_registration_table = $otp_registration_table; |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
|
|
/** |
|
79
|
|
|
* Get a language key for this specific module. |
|
80
|
|
|
* @return string |
|
81
|
|
|
*/ |
|
82
|
|
|
public function get_translatable_name() |
|
83
|
|
|
{ |
|
84
|
|
|
return 'OTP'; |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Return the name of the current module |
|
89
|
|
|
* This is for internal use only |
|
90
|
|
|
* @return string |
|
91
|
|
|
*/ |
|
92
|
|
|
public function get_name() |
|
93
|
|
|
{ |
|
94
|
|
|
return 'otp'; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
/** |
|
98
|
|
|
* Return if this module is enabled by the admin |
|
99
|
|
|
* (And all server requirements are met). |
|
100
|
|
|
* |
|
101
|
|
|
* Do not return false in case a specific user disabled this module, |
|
102
|
|
|
* OR if the user is unable to use this specific module, |
|
103
|
|
|
* OR if a browser specific item is missing/incorrect. |
|
104
|
|
|
* @return boolean |
|
105
|
|
|
*/ |
|
106
|
|
|
public function is_enabled() |
|
107
|
|
|
{ |
|
108
|
|
|
return true; |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
/** |
|
112
|
|
|
* Check if the current user is able to use this module. |
|
113
|
|
|
* |
|
114
|
|
|
* This means that the user enabled it in the UCP, |
|
115
|
|
|
* And has it setup up correctly. |
|
116
|
|
|
* This method will be called during login, not during registration/ |
|
117
|
|
|
* |
|
118
|
|
|
* @param int $user_id |
|
119
|
|
|
* |
|
120
|
|
|
* @return bool |
|
121
|
|
|
*/ |
|
122
|
|
View Code Duplication |
public function is_usable($user_id) |
|
|
|
|
|
|
123
|
|
|
{ |
|
124
|
|
|
$sql = 'SELECT COUNT(registration_id) as reg_id |
|
125
|
|
|
FROM ' . $this->otp_registration_table . ' |
|
126
|
|
|
WHERE |
|
127
|
|
|
user_id = ' . (int) $user_id; |
|
128
|
|
|
$result = $this->db->sql_query($sql); |
|
129
|
|
|
$row = $this->db->sql_fetchrow($result); |
|
130
|
|
|
$this->db->sql_freeresult($result); |
|
131
|
|
|
|
|
132
|
|
|
return $row && $row['reg_id'] > 0; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* Check if the user can potentially use this. |
|
137
|
|
|
* This method is called at registration page. |
|
138
|
|
|
* |
|
139
|
|
|
* You can, for example, check if the current browser is suitable. |
|
140
|
|
|
* |
|
141
|
|
|
* @param int|boolean $user_id Use false to ignore user |
|
142
|
|
|
* |
|
143
|
|
|
* @return bool |
|
144
|
|
|
*/ |
|
145
|
|
|
public function is_potentially_usable($user_id = false) |
|
146
|
|
|
{ |
|
147
|
|
|
return true; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* Get the priority for this module. |
|
152
|
|
|
* A lower priority means more chance it gets selected as default option |
|
153
|
|
|
* |
|
154
|
|
|
* There can be only one module with a specific priority! |
|
155
|
|
|
* If there is already a module registered with this priority, |
|
156
|
|
|
* a Exception might be thrown |
|
157
|
|
|
* |
|
158
|
|
|
* @return int |
|
159
|
|
|
*/ |
|
160
|
|
|
public function get_priority() |
|
161
|
|
|
{ |
|
162
|
|
|
return 15; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
/** |
|
166
|
|
|
* Start of the login procedure. |
|
167
|
|
|
* |
|
168
|
|
|
* @param int $user_id |
|
169
|
|
|
* |
|
170
|
|
|
* @return int |
|
171
|
|
|
*/ |
|
172
|
|
|
public function login_start($user_id) |
|
173
|
|
|
{ |
|
174
|
|
|
// TODO: Implement login_start() method. |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* Actual login procedure |
|
179
|
|
|
* |
|
180
|
|
|
* @param int $user_id |
|
181
|
|
|
*/ |
|
182
|
|
|
public function login($user_id) |
|
183
|
|
|
{ |
|
184
|
|
|
// TODO: Implement login() method. |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* If this module can add new keys (Or other things) |
|
189
|
|
|
* |
|
190
|
|
|
* @return boolean |
|
191
|
|
|
*/ |
|
192
|
|
|
public function can_register() |
|
193
|
|
|
{ |
|
194
|
|
|
return true; |
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
|
|
/** |
|
198
|
|
|
* Start with the registration of a new security key. |
|
199
|
|
|
* This page should return a name of a template, and |
|
200
|
|
|
* it should assign the required variables for this template. |
|
201
|
|
|
* |
|
202
|
|
|
* @return string |
|
203
|
|
|
*/ |
|
204
|
|
|
public function register_start() |
|
205
|
|
|
{ |
|
206
|
|
|
$secret = $this->otp->generateSecret(); |
|
207
|
|
|
$QR = $this->otp_helper->generateKeyURI('totp', $secret, generate_board_url()); |
|
208
|
|
|
$this->template->assign_vars(array( |
|
209
|
|
|
'TFA_QR_CODE' => 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=' . $QR, |
|
210
|
|
|
'TFA_SECRET' => $secret, |
|
211
|
|
|
'L_TFA_ADD_OTP_KEY_EXPLAIN' => $this->user->lang('TFA_ADD_OTP_KEY_EXPLAIN', $secret), |
|
212
|
|
|
'S_HIDDEN_FIELDS' => build_hidden_fields(array( |
|
213
|
|
|
'secret' => $secret, |
|
214
|
|
|
)), |
|
215
|
|
|
)); |
|
216
|
|
|
|
|
217
|
|
|
return 'tfa_otp_ucp_new'; |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
/** |
|
221
|
|
|
* Do the actual registration of a new security key. |
|
222
|
|
|
* |
|
223
|
|
|
* @return boolean Result of the registration. |
|
224
|
|
|
* @throws BadRequestHttpException |
|
225
|
|
|
*/ |
|
226
|
|
|
public function register() |
|
227
|
|
|
{ |
|
228
|
|
|
$secret = $this->request->variable('secret', ''); |
|
229
|
|
|
$otp = $this->request->variable('otp', ''); |
|
230
|
|
|
|
|
231
|
|
|
if (!$this->otp->checkTOTP($secret, $otp)) |
|
232
|
|
|
{ |
|
233
|
|
|
throw new BadRequestHttpException($this->user->lang('TFA_OTP_INVALID_KEY')); |
|
234
|
|
|
} |
|
235
|
|
|
|
|
236
|
|
|
$sql_ary = array( |
|
237
|
|
|
'user_id' => $this->user->data['user_id'], |
|
238
|
|
|
'secret' => $secret, |
|
239
|
|
|
'registered' => time(), |
|
240
|
|
|
'last_used' => time(), |
|
241
|
|
|
); |
|
242
|
|
|
|
|
243
|
|
|
$sql = 'INSERT INTO ' . $this->otp_registration_table . ' ' . $this->db->sql_build_array('INSERT', $sql_ary); |
|
244
|
|
|
$this->db->sql_query($sql); |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
/** |
|
248
|
|
|
* This method is called to show the UCP page. |
|
249
|
|
|
* You can assign template variables to the template, or do anything else here. |
|
250
|
|
|
*/ |
|
251
|
|
|
public function show_ucp() |
|
252
|
|
|
{ |
|
253
|
|
|
$this->show_ucp_complete($this->otp_registration_table); |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* Delete a specific row from the UCP. |
|
258
|
|
|
* The data is based on the data provided in show_ucp. |
|
259
|
|
|
* |
|
260
|
|
|
* @param int $key |
|
261
|
|
|
* |
|
262
|
|
|
* @return void |
|
263
|
|
|
*/ |
|
264
|
|
View Code Duplication |
public function delete($key) |
|
|
|
|
|
|
265
|
|
|
{ |
|
266
|
|
|
$sql = 'DELETE FROM ' . $this->otp_registration_table . ' |
|
267
|
|
|
WHERE user_id = ' . (int) $this->user->data['user_id'] . ' |
|
268
|
|
|
AND registration_id =' . (int) $key; |
|
269
|
|
|
|
|
270
|
|
|
$this->db->sql_query($sql); |
|
271
|
|
|
} |
|
272
|
|
|
} |
|
273
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.