ThreemaGateway_Handler_Action_TfaCallback_Abstract   C
last analyzed

Complexity

Total Complexity 54

Size/Duplication

Total Lines 675
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
wmc 54
lcom 2
cbo 3
dl 0
loc 675
rs 5.7474
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 1
prepareProcessing() 0 1 ?
A applyFilters() 0 15 4
C processPending() 0 32 8
A processConfirmRequest() 0 11 2
A setMessageTypeName() 0 5 1
A setPrendingRequestType() 0 4 1
A getReferencedData() 0 5 1
A addFilter() 0 8 1
applyFilter() 0 1 ?
A preSaveData() 0 4 1
A preSaveDataMerged() 0 4 1
A postSaveData() 0 4 1
A preProcessPending() 0 10 2
A postProcessPending() 0 9 2
A getPendingRequests() 0 19 2
A messageIsExpired() 0 12 2
C setDataForRequest() 0 80 7
A getProviderDataBySession() 0 18 2
B getProviderDataByModel() 0 25 4
B saveProviderData() 0 25 3
A getModelFromCache() 0 8 2
A getSession() 0 9 2
A log() 0 4 1
A getUserData() 0 16 3

How to fix   Complexity   

Complex Class

Complex classes like ThreemaGateway_Handler_Action_TfaCallback_Abstract often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ThreemaGateway_Handler_Action_TfaCallback_Abstract, and based on these observations, apply Extract Interface, too.

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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->pendingRequests of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
166
            $this->preProcessPending();
167
        }
168
        if (!$this->pendingRequests) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->pendingRequests of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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)) {
0 ignored issues
show
Unused Code introduced by
The call to ThreemaGateway_Handler_A...processConfirmRequest() has too many arguments starting with $processOptions.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
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 = [])
0 ignored issues
show
Unused Code introduced by
The parameter $confirmRequest is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 = [])
0 ignored issues
show
Unused Code introduced by
The parameter $confirmRequest is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $providerData is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $processOptions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
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()) {
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getPendingRequests() can also be of type false. However, the property $pendingRequests is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $userTfa of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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
0 ignored issues
show
Documentation introduced by
Should the return type not be XenForo_Session|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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