1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* 2FA callback actions. |
4
|
|
|
* |
5
|
|
|
* @package ThreemaGateway |
6
|
|
|
* @author rugk |
7
|
|
|
* @copyright Copyright (c) 2015-2016 rugk |
8
|
|
|
* @license MIT |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
abstract class ThreemaGateway_Handler_Action_TfaCallback_Abstract extends ThreemaGateway_Handler_Action_Abstract |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* @var array Cache of models |
15
|
|
|
*/ |
16
|
|
|
protected $modelCache = []; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var XenForo_Session|null Imitiated session |
20
|
|
|
*/ |
21
|
|
|
protected $session = null; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var array user data cache |
25
|
|
|
*/ |
26
|
|
|
protected $user = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var string how the provider data has been fetched |
30
|
|
|
*/ |
31
|
|
|
protected $dataFetchMode; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var ThreemaGateway_Handler_Action_Callback |
35
|
|
|
*/ |
36
|
|
|
protected $callback; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var Threema\MsgApi\Helpers\ReceiveMessageResult |
40
|
|
|
*/ |
41
|
|
|
protected $receiveResult; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var Threema\MsgApi\Messages\ThreemaMessage |
45
|
|
|
*/ |
46
|
|
|
protected $threemaMsg; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var array|string |
50
|
|
|
*/ |
51
|
|
|
protected $log; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var bool |
55
|
|
|
*/ |
56
|
|
|
protected $saveMessage; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var bool |
60
|
|
|
*/ |
61
|
|
|
protected $debugMode; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var string name of the processed message |
65
|
|
|
*/ |
66
|
|
|
protected $name = 'message'; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var string name of the secret contained in a message |
70
|
|
|
*/ |
71
|
|
|
protected $nameSecret = 'data'; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var int the type of the request |
75
|
|
|
*/ |
76
|
|
|
protected $pendingRequestType; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var array filters/conditions applied to the message |
80
|
|
|
*/ |
81
|
|
|
protected $filters; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var array all pending request messages found for the current message |
85
|
|
|
*/ |
86
|
|
|
protected $pendingRequests; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Checks whether text messages contain code used for the receiver 2FA. |
90
|
|
|
* |
91
|
|
|
* You should set the "event hint" to "1" to only pass text messages to the |
92
|
|
|
* listener. Otherwise errors may happen. |
93
|
|
|
* |
94
|
|
|
* @param ThreemaGateway_Handler_Action_Callback $handler |
95
|
|
|
* @param Threema\MsgApi\Helpers\ReceiveMessageResult $receiveResult |
96
|
|
|
* @param Threema\MsgApi\Messages\ThreemaMessage $threemaMsg |
97
|
|
|
* @param array|string $output [$logType, $debugLog, $publicLog] |
98
|
|
|
* @param bool $saveMessage |
99
|
|
|
* @param bool $debugMode |
100
|
|
|
* |
101
|
|
|
* @throws XenForo_Exception |
102
|
|
|
*/ |
103
|
|
|
public function __construct(ThreemaGateway_Handler_Action_Callback $handler, |
104
|
|
|
Threema\MsgApi\Helpers\ReceiveMessageResult $receiveResult, |
105
|
|
|
Threema\MsgApi\Messages\ThreemaMessage $threemaMsg, |
106
|
|
|
&$output, |
107
|
|
|
&$saveMessage, |
108
|
|
|
$debugMode) |
109
|
|
|
{ |
110
|
|
|
$this->callback = $handler; |
111
|
|
|
$this->log = $output; |
112
|
|
|
$this->receiveResult = $receiveResult; |
113
|
|
|
$this->threemaMsg = $threemaMsg; |
114
|
|
|
$this->saveMessage = $saveMessage; |
115
|
|
|
$this->debugMode = $debugMode; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Prepare the message handling. |
120
|
|
|
* |
121
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
122
|
|
|
* |
123
|
|
|
* @throws XenForo_Exception |
124
|
|
|
* @return bool |
125
|
|
|
*/ |
126
|
|
|
abstract public function prepareProcessing(); |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Filters the passed data/message. |
130
|
|
|
* |
131
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
132
|
|
|
* The filters have had to be set by [@see addFilter()] before. |
133
|
|
|
* |
134
|
|
|
* @return bool |
135
|
|
|
*/ |
136
|
|
|
public function applyFilters() |
137
|
|
|
{ |
138
|
|
|
// skip check if there are no filters |
139
|
|
|
if (!$this->filters) { |
|
|
|
|
140
|
|
|
return true; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
foreach ($this->filters as $filter) { |
144
|
|
|
if (!$this->applyFilter($filter['type'], $filter['data'], $filter['fail'])) { |
145
|
|
|
return false; |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
return true; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Processes the pending requests, i.e. iterates all confirm requests and handles |
154
|
|
|
* them. |
155
|
|
|
* |
156
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
157
|
|
|
* |
158
|
|
|
* @param array $processOptions options, which are passed to {@link processConfirmRequest()}. |
159
|
|
|
* @throws XenForo_Exception |
160
|
|
|
* |
161
|
|
|
* @return bool |
162
|
|
|
*/ |
163
|
|
|
public function processPending(array $processOptions = []) |
164
|
|
|
{ |
165
|
|
|
if (!$this->pendingRequests) { |
|
|
|
|
166
|
|
|
$this->preProcessPending(); |
167
|
|
|
} |
168
|
|
|
if (!$this->pendingRequests) { |
|
|
|
|
169
|
|
|
if (isset($processOptions['requirePendingRequests']) && $processOptions['requirePendingRequests']) { |
170
|
|
|
throw new XenForo_Exception('preProcessPending() could not get any pending request data.'); |
171
|
|
|
} |
172
|
|
|
return false; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
// handle all requests |
176
|
|
|
/** @var bool $success whether the request has been successfully processed */ |
177
|
|
|
$success = false; |
178
|
|
|
|
179
|
|
|
foreach ($this->pendingRequests as $confirmRequest) { |
180
|
|
|
// now confirm request |
181
|
|
|
if (!$this->processConfirmRequest($confirmRequest, $processOptions)) { |
|
|
|
|
182
|
|
|
// in case of error, just skip the message |
183
|
|
|
continue; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$success = true; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
if (!$this->postProcessPending($success)) { |
190
|
|
|
return false; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
return $success; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Verifies & saves data for one confirm request. |
198
|
|
|
* |
199
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
200
|
|
|
* childs should call the parent here as the things done in this class are |
201
|
|
|
* essential! |
202
|
|
|
* |
203
|
|
|
* @throws XenForo_Exception |
204
|
|
|
* @return bool |
205
|
|
|
*/ |
206
|
|
|
protected function processConfirmRequest($confirmRequest) |
207
|
|
|
{ |
208
|
|
|
// first verify send time |
209
|
|
|
if ($this->messageIsExpired($confirmRequest)) { |
210
|
|
|
continue; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
// other processing should be done by child |
214
|
|
|
|
215
|
|
|
return true; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Sets the name of the processed message. |
220
|
|
|
* |
221
|
|
|
* @param string $name what message this is, e.g.: XY message |
222
|
|
|
* @param string $nameSecret what secrets etc. conmtains the message: XY code |
223
|
|
|
*/ |
224
|
|
|
final public function setMessageTypeName($name, $nameSecret) |
225
|
|
|
{ |
226
|
|
|
$this->name = $name; |
227
|
|
|
$this->nameSecret = $nameSecret; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Sets the type of the request. |
232
|
|
|
* |
233
|
|
|
* Use one of the constants in |
234
|
|
|
* ThreemaGateway_Model_TfaPendingMessagesConfirmation::PENDING_* |
235
|
|
|
* |
236
|
|
|
* @param int $pendingRequestType |
237
|
|
|
*/ |
238
|
|
|
final public function setPrendingRequestType($pendingRequestType) |
239
|
|
|
{ |
240
|
|
|
$this->pendingRequestType = $pendingRequestType; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Returns the log and save message data you passed to this class when initiating. |
245
|
|
|
* |
246
|
|
|
* This should be called at least once at the end as this is the only way |
247
|
|
|
* to update the log and save message values. |
248
|
|
|
* |
249
|
|
|
* @param array|string $output [$logType, $debugLog, $publicLog] |
250
|
|
|
* @param bool $saveMessage |
251
|
|
|
*/ |
252
|
|
|
final public function getReferencedData(&$output, &$saveMessage) |
253
|
|
|
{ |
254
|
|
|
$output = $this->log; |
255
|
|
|
$saveMessage = $this->saveMessage; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Adds a filter, which is applied to the message (data) when |
260
|
|
|
* {@see applyFilter()} is called. |
261
|
|
|
* |
262
|
|
|
* @param int $filterType please use the constants FILTER_* |
263
|
|
|
* @param mixed $filterData any data the filter uses |
264
|
|
|
* @param bool $failOnError whether the filter should fail on errors (true) |
265
|
|
|
* or silently ignore them (false) |
266
|
|
|
*/ |
267
|
|
|
public function addFilter($filterType, $filterData, $failOnError = true) |
268
|
|
|
{ |
269
|
|
|
$this->filters[] = [ |
270
|
|
|
'type' => $filterType, |
271
|
|
|
'data' => $filterData, |
272
|
|
|
'fail' => $failOnError |
273
|
|
|
]; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Filters the passed data/message. |
278
|
|
|
* |
279
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
280
|
|
|
* |
281
|
|
|
* @param int $filterType please use the constants FILTER_* |
282
|
|
|
* @param mixed $filterData any data the filter uses |
283
|
|
|
* @param bool $failOnError whether the filter should fail on errors (true) |
284
|
|
|
* or silently ignore them (false) |
285
|
|
|
* |
286
|
|
|
* @throws XenForo_Exception |
287
|
|
|
* @return bool |
288
|
|
|
*/ |
289
|
|
|
abstract protected function applyFilter($filterType, $filterData, $failOnError = true); |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Analyses/filters/validates the existing old provider data e.g. to |
293
|
|
|
* compare it with the new data to set. |
294
|
|
|
* |
295
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
296
|
|
|
* |
297
|
|
|
* @param array $confirmRequest the confirm request |
298
|
|
|
* @param array $oldProviderData old data read |
299
|
|
|
* @param array $setData new data to set |
300
|
|
|
* @param array $processOptions custom options (optional) |
301
|
|
|
* |
302
|
|
|
* @throws XenForo_Exception |
303
|
|
|
* @return bool |
304
|
|
|
*/ |
305
|
|
|
protected function preSaveData(array $confirmRequest, array &$oldProviderData, array &$setData, array $processOptions = []) |
|
|
|
|
306
|
|
|
{ |
307
|
|
|
return true; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Handles the already merged provider data. |
312
|
|
|
* |
313
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
314
|
|
|
* |
315
|
|
|
* @param array $confirmRequest the confirm request |
316
|
|
|
* @param array $providerData merged provider data |
317
|
|
|
* @param array $processOptions custom options (optional) |
318
|
|
|
* |
319
|
|
|
* @throws XenForo_Exception |
320
|
|
|
* @return bool |
321
|
|
|
*/ |
322
|
|
|
protected function preSaveDataMerged(array $confirmRequest, array &$providerData, array $processOptions = []) |
323
|
|
|
{ |
324
|
|
|
return true; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Does some stuff with the data after it has been saved. |
329
|
|
|
* |
330
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
331
|
|
|
* |
332
|
|
|
* @param array $confirmRequest the confirm request |
333
|
|
|
* @param array $providerData old data read |
334
|
|
|
* @param array $processOptions custom options (optional) |
335
|
|
|
* |
336
|
|
|
* @throws XenForo_Exception |
337
|
|
|
* @return bool |
338
|
|
|
*/ |
339
|
|
|
protected function postSaveData(array $confirmRequest, array &$providerData, array $processOptions = []) |
|
|
|
|
340
|
|
|
{ |
341
|
|
|
return true; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Does stuff needed to be done before processing the actual requests. |
346
|
|
|
* |
347
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
348
|
|
|
* Childs should call the parent here as the things done in this class are |
349
|
|
|
* essential! |
350
|
|
|
* |
351
|
|
|
* @throws XenForo_Exception |
352
|
|
|
* @return bool |
|
|
|
|
353
|
|
|
*/ |
354
|
|
|
protected function preProcessPending() |
355
|
|
|
{ |
356
|
|
|
// check whether message has already been saved to prevent replay attacks |
357
|
|
|
$this->callback->assertNoReplayAttack($this->receiveResult->getMessageId()); |
358
|
|
|
|
359
|
|
|
/** @var array|false $this->pendingRequests all pending requests (or false if there are none) */ |
360
|
|
|
if (!$this->pendingRequests = $this->getPendingRequests()) { |
|
|
|
|
361
|
|
|
return false; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Does stuff, which needs to be done after processing the requests. |
367
|
|
|
* |
368
|
|
|
* Returns "false" if the process should be canceled. Otherwise "true". |
369
|
|
|
* Childs should call the parent here as the things done in this class are |
370
|
|
|
* essential! |
371
|
|
|
* |
372
|
|
|
* @param bool $success whether the data was processed successfully |
373
|
|
|
* @throws XenForo_Exception |
374
|
|
|
* @return bool |
375
|
|
|
*/ |
376
|
|
|
protected function postProcessPending($success) |
377
|
|
|
{ |
378
|
|
|
if ($success) { |
379
|
|
|
// do not save message as it already has been processed |
380
|
|
|
$this->saveMessage = false; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
return true; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Returns the pending messages for a given. |
388
|
|
|
* |
389
|
|
|
* @return array|false |
390
|
|
|
*/ |
391
|
|
|
protected function getPendingRequests() |
392
|
|
|
{ |
393
|
|
|
/** @var ThreemaGateway_Model_TfaPendingMessagesConfirmation $pendingRequestsModel */ |
394
|
|
|
$pendingRequestsModel = $this->getModelFromCache('ThreemaGateway_Model_TfaPendingMessagesConfirmation'); |
395
|
|
|
|
396
|
|
|
/** @var array|null $pendingRequests all pending requests if there are some */ |
397
|
|
|
$pendingRequests = $pendingRequestsModel->getPending( |
398
|
|
|
$this->callback->getRequest('from'), |
399
|
|
|
null, |
400
|
|
|
$this->pendingRequestType |
401
|
|
|
); |
402
|
|
|
|
403
|
|
|
if (!$pendingRequests) { |
404
|
|
|
$this->log('No confirmation requests registered. Abort.'); |
405
|
|
|
return false; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
return $pendingRequests; |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Checks whether a message is expired. |
413
|
|
|
* |
414
|
|
|
* This uses the date given by the Threema Gateway server (this is the |
415
|
|
|
* send date) to verify that the message is not expired. |
416
|
|
|
* Thus the current date is not used for this comparison as this should |
417
|
|
|
* be done in the 2FA provider directly when verifying the data |
418
|
|
|
* (verifyFromInput). |
419
|
|
|
* |
420
|
|
|
* @param array $confirmRequest the confirmation message request |
421
|
|
|
* @throws XenForo_Exception |
422
|
|
|
* |
423
|
|
|
* @return bool |
424
|
|
|
*/ |
425
|
|
|
protected function messageIsExpired(array $confirmRequest) |
426
|
|
|
{ |
427
|
|
|
if ($this->callback->getRequest('date') > $confirmRequest['expiry_date']) { |
428
|
|
|
$this->log( |
429
|
|
|
'Message is too old.', |
430
|
|
|
'Message is too old, already expired. Maximum: ' . date('Y-m-d H:i:s', $confirmRequest['expiry_date']) |
431
|
|
|
); |
432
|
|
|
return true; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
return false; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* Saves data for a confirm request (as the provider data of the 2FA method). |
440
|
|
|
* |
441
|
|
|
* @param array $confirmRequest the confirmation message request |
442
|
|
|
* @param array $setData an array of the data to set |
443
|
|
|
* @param array $processOptions custom options (optional) |
444
|
|
|
* @throws XenForo_Exception |
445
|
|
|
*/ |
446
|
|
|
protected function setDataForRequest( |
447
|
|
|
array $confirmRequest, |
448
|
|
|
array $setData, |
449
|
|
|
array $processOptions = [] |
450
|
|
|
) { |
451
|
|
|
/** @var array $providerData provider data of session */ |
452
|
|
|
$providerData = []; |
453
|
|
|
|
454
|
|
|
$this->log('', 'Request #' . |
455
|
|
|
$confirmRequest['request_id'] . ' from ' . |
456
|
|
|
$confirmRequest['provider_id'] . ' for user ' . |
457
|
|
|
$confirmRequest['user_id'] . ' for session ' . |
458
|
|
|
$confirmRequest['session_id']); |
459
|
|
|
|
460
|
|
|
// clear potentially old session data |
461
|
|
|
$this->session = null; |
462
|
|
|
|
463
|
|
|
try { |
464
|
|
|
$providerData = $this->getProviderDataBySession($confirmRequest); |
465
|
|
|
|
466
|
|
|
$this->log( |
467
|
|
|
'', |
468
|
|
|
'Got provider data from session.' |
469
|
|
|
); |
470
|
|
|
} catch (Exception $e) { |
471
|
|
|
$this->log( |
472
|
|
|
'', |
473
|
|
|
$e->getMessage() . ' Try 2FA model.' |
474
|
|
|
); |
475
|
|
|
|
476
|
|
|
// second try via model |
477
|
|
|
try { |
478
|
|
|
$providerData = $this->getProviderDataByModel($confirmRequest); |
479
|
|
|
|
480
|
|
|
$this->log( |
481
|
|
|
'', |
482
|
|
|
'Got provider data from user model.' |
483
|
|
|
); |
484
|
|
|
} catch (Exception $e) { |
485
|
|
|
$this->log( |
486
|
|
|
'Could not get provider data.', |
487
|
|
|
$e->getMessage() . ' Abort.' |
488
|
|
|
); |
489
|
|
|
|
490
|
|
|
// re-throw exception |
491
|
|
|
throw $e; |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
if (!$this->preSaveData($confirmRequest, $providerData, $setData, $processOptions)) { |
496
|
|
|
throw new Exception('preSaveData() returned an error and prevented data saving.'); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
// merge the data with the original provider data |
500
|
|
|
$providerData = array_merge($providerData, $setData); |
501
|
|
|
|
502
|
|
|
if (!$this->preSaveDataMerged($confirmRequest, $providerData, $processOptions)) { |
503
|
|
|
throw new Exception('preSaveDataMerged() returned an error and prevented data saving.'); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
// and save the data |
507
|
|
|
try { |
508
|
|
|
$this->saveProviderData($providerData, $confirmRequest); |
509
|
|
|
} catch (Exception $e) { |
510
|
|
|
$this->log('Could not save provider data.', $e->getMessage()); |
511
|
|
|
|
512
|
|
|
// re-throw exception |
513
|
|
|
throw $e; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
$this->log($this->nameSecret . ' saved.', 'Saved ' . $this->nameSecret . ' for request #' . |
517
|
|
|
$confirmRequest['request_id'] . ' from ' . |
518
|
|
|
$confirmRequest['provider_id'] . ' for user ' . |
519
|
|
|
$confirmRequest['user_id'] . ' for session ' . |
520
|
|
|
$confirmRequest['session_id']); |
521
|
|
|
|
522
|
|
|
if (!$this->postSaveData($confirmRequest, $providerData, $processOptions)) { |
523
|
|
|
throw new Exception('postSaveData() returned an error.'); |
524
|
|
|
} |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
/** |
528
|
|
|
* Fetches and returns the provider data using the session. |
529
|
|
|
* |
530
|
|
|
* @param array $confirmRequest the confirmation message request |
531
|
|
|
* @throws XenForo_Exception |
532
|
|
|
* @return array |
533
|
|
|
*/ |
534
|
|
|
protected function getProviderDataBySession(array $confirmRequest) |
535
|
|
|
{ |
536
|
|
|
/** @var XenForo_Session $session */ |
537
|
|
|
$session = $this->getSession(); |
538
|
|
|
$session->threemagwSetupRaw($confirmRequest['session_id'], false); |
539
|
|
|
|
540
|
|
|
/** @var string $sessionKey session key identifying */ |
541
|
|
|
$sessionKey = 'tfaData_' . $confirmRequest['provider_id']; |
542
|
|
|
/** @var array $providerData provider data of session */ |
543
|
|
|
$providerData = $session->get($sessionKey); |
544
|
|
|
|
545
|
|
|
if (empty($providerData)) { |
546
|
|
|
throw new XenForo_Exception('Could not get provider data from session using key ' . $sessionKey . '.'); |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
$this->dataFetchMode = 'session'; |
550
|
|
|
return $providerData; |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
/** |
554
|
|
|
* Fetches and returns the provider data. |
555
|
|
|
* |
556
|
|
|
* @param array $confirmRequest the confirmation message request |
557
|
|
|
* @throws XenForo_Exception |
558
|
|
|
* @return array |
559
|
|
|
*/ |
560
|
|
|
protected function getProviderDataByModel(array $confirmRequest) |
561
|
|
|
{ |
562
|
|
|
/** @var XenForo_Model_Tfa $tfaModel */ |
563
|
|
|
$tfaModel = $this->getModelFromCache('XenForo_Model_Tfa'); |
564
|
|
|
|
565
|
|
|
/** @var array $userTfa */ |
566
|
|
|
$userTfa = $tfaModel->getUserTfaEntries($confirmRequest['user_id']); |
567
|
|
|
if (!$userTfa) { |
|
|
|
|
568
|
|
|
throw new XenForo_Exception('Could not get user 2FA data.'); |
569
|
|
|
} |
570
|
|
|
|
571
|
|
|
try { |
572
|
|
|
/** @var array $providerData provider data of session */ |
573
|
|
|
$providerData = unserialize($userTfa[$confirmRequest['provider_id']]['provider_data']); |
574
|
|
|
} catch (Exception $e) { |
575
|
|
|
throw new XenForo_Exception('Could not get provider data. (error: ' . $e->getMessage() . ')'); |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
if (empty($providerData)) { |
579
|
|
|
throw new XenForo_Exception('Could not get provider data.'); |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
$this->dataFetchMode = 'tfa_model'; |
583
|
|
|
return $providerData; |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* Gets model from cache or initializes a new model if needed. |
588
|
|
|
* |
589
|
|
|
* @param array $newProviderData provider data to save |
590
|
|
|
* @param array $confirmRequest the confirmation message request |
591
|
|
|
* |
592
|
|
|
* @throws XenForo_Exception |
593
|
|
|
*/ |
594
|
|
|
protected function saveProviderData(array $newProviderData, array $confirmRequest) |
595
|
|
|
{ |
596
|
|
|
switch ($this->dataFetchMode) { |
597
|
|
|
case 'session': |
598
|
|
|
/** @var string $sessionKey session key identifying */ |
599
|
|
|
$sessionKey = 'tfaData_' . $confirmRequest['provider_id']; |
600
|
|
|
|
601
|
|
|
/** @var XenForo_Session $session */ |
602
|
|
|
$session = $this->getSession(); |
603
|
|
|
|
604
|
|
|
$session->set($sessionKey, $newProviderData); |
605
|
|
|
$session->save(); |
606
|
|
|
break; |
607
|
|
|
|
608
|
|
|
case 'tfa_model': |
609
|
|
|
/** @var XenForo_Model_Tfa $tfaModel */ |
610
|
|
|
$tfaModel = $this->getModelFromCache('XenForo_Model_Tfa'); |
611
|
|
|
$tfaModel->updateUserProvider($confirmRequest['user_id'], $confirmRequest['provider_id'], $newProviderData, false); |
612
|
|
|
break; |
613
|
|
|
|
614
|
|
|
default: |
615
|
|
|
// if all fails, we can only throw an exception |
616
|
|
|
throw new XenForo_Exception('Invalid provider data fetch method: ' . $this->dataFetchMode); |
617
|
|
|
} |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* Gets model from cache or initializes a new model if needed. |
622
|
|
|
* |
623
|
|
|
* @param string $class Name of class to load |
624
|
|
|
* |
625
|
|
|
* @return XenForo_Model |
626
|
|
|
*/ |
627
|
|
|
final protected function getModelFromCache($class) |
628
|
|
|
{ |
629
|
|
|
if (!isset($this->modelCache[$class])) { |
630
|
|
|
$this->modelCache[$class] = XenForo_Model::create($class); |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
return $this->modelCache[$class]; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* Returns the XenForo session. |
638
|
|
|
* |
639
|
|
|
* @return XenForo_Session |
640
|
|
|
*/ |
641
|
|
|
final protected function getSession() |
642
|
|
|
{ |
643
|
|
|
if (!$this->session) { |
644
|
|
|
$class = XenForo_Application::resolveDynamicClass('XenForo_Session'); |
645
|
|
|
$this->session = new $class; |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
return $this->session; |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Adds some data to the log. |
653
|
|
|
* |
654
|
|
|
* @param string $logDetailed |
655
|
|
|
* @param string|null $logGeneral |
656
|
|
|
* @return XenForo_Session |
|
|
|
|
657
|
|
|
*/ |
658
|
|
|
final protected function log($logDetailed, $logGeneral = null) |
659
|
|
|
{ |
660
|
|
|
return $this->callback->addLog($this->log, $logDetailed, $logGeneral); |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
/** |
664
|
|
|
* Returns the user array. |
665
|
|
|
* |
666
|
|
|
* @param int $userId |
667
|
|
|
* @return array |
668
|
|
|
*/ |
669
|
|
|
final protected function getUserData($userId) |
670
|
|
|
{ |
671
|
|
|
if (!isset($this->user[$userId])) { |
672
|
|
|
/** @var XenForo_Model_User $userModel */ |
673
|
|
|
$userModel = $this->getModelFromCache('XenForo_Model_User'); |
674
|
|
|
/** @var array $user */ |
675
|
|
|
$user = $userModel->getFullUserById($userId); |
676
|
|
|
if (!$user) { |
|
|
|
|
677
|
|
|
throw new XenForo_Exception('Could not get user data data.'); |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
$this->user[$userId] = $user; |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
return $this->user[$userId]; |
684
|
|
|
} |
685
|
|
|
} |
686
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.