DeliveryReceipt   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 3
dl 0
loc 220
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A prepareProcessing() 0 7 1
B applyFilter() 0 28 6
A preProcessPending() 0 12 2
B processConfirmRequest() 0 44 6
A preSaveData() 0 14 4
B preSaveDataMerged() 0 24 4
1
<?php
2
/**
3
 * 2FA callback action for handeling delivery receipt messages.
4
 *
5
 * @package ThreemaGateway
6
 * @author rugk
7
 * @copyright Copyright (c) 2015-2016 rugk
8
 * @license MIT
9
 */
10
11
class ThreemaGateway_Handler_Action_TfaCallback_DeliveryReceipt extends ThreemaGateway_Handler_Action_TfaCallback_Abstract
12
{
13
    /**
14
     * @var int filter because of receipt type
15
     */
16
    const FILTER_RECEIPT_TYPE_EQUAL = 1;
17
18
    /**
19
     * @var int filter because of receipt type
20
     */
21
    const FILTER_RECEIPT_TYPE_MORETHAN = 2;
22
23
    /**
24
     * @var int filter because of receipt type
25
     */
26
    const FILTER_RECEIPT_TYPE_LESSTHAN = 3;
27
28
    /**
29
     * @var array acknowledged message IDs
30
     */
31
    protected $ackedMsgIds;
32
33
    /**
34
     * @var int type of delivery receipt
35
     */
36
    protected $receiptType;
37
38
    /**
39
     * Prepare the message handling. Should be called before any other actions.
40
     *
41
     * @return bool
42
     */
43
    public function prepareProcessing()
44
    {
45
        $this->ackedMsgIds = $this->threemaMsg->getAckedMessageIds();
46
        $this->receiptType = $this->threemaMsg->getReceiptType();
47
48
        return true;
49
    }
50
51
    /**
52
     * Filters the passed data/message.
53
     *
54
     * Returns "false" if the process should be canceled. Otherwise "true".
55
     *
56
     * @param int   $filterType  please use the constants FILTER_*
57
     * @param mixed $filterData  any data the filter uses
58
     * @param bool  $failOnError whether the filter should fail on errors (true)
59
     *                           or silently ignore them (false)
60
     *
61
     * @throws XenForo_Exception
62
     * @return bool
63
     */
64
    protected function applyFilter($filterType, $filterData, $failOnError = true)
65
    {
66
        /** @var $success bool */
67
        $success = true;
0 ignored issues
show
Unused Code introduced by
$success is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
68
69
        switch ($filterType) {
70
            case $this::FILTER_RECEIPT_TYPE_EQUAL:
71
                $success = ($this->receiptType == $filterData);
72
                break;
73
74
            case $this::FILTER_RECEIPT_TYPE_MORETHAN:
75
                $success = ($this->receiptType > $filterData);
76
                break;
77
78
            case $this::FILTER_RECEIPT_TYPE_LESSTHAN:
79
                $success = ($this->receiptType < $filterData);
80
                break;
81
82
            default:
83
                throw new XenForo_Exception('Unknown filter type: ' . $filterType);
84
        }
85
86
        if ($failOnError && !$success) {
87
            return false;
88
        }
89
90
        return true;
91
    }
92
93
    /**
94
     * Does all steps needed to do before processing data.
95
     *
96
     * Returns "false" if the process should be canceled. Otherwise "true".
97
     *
98
     * @throws XenForo_Exception
99
     * @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...
100
     */
101
    protected function preProcessPending()
102
    {
103
        // as the acknowledged message IDs cannot be evaluated at this point of
104
        // time one cannot be sure that th acknowledged message really belongs
105
        // to a 2FA message.
106
        // ThatÄs the reason for the "potential" here.
107
        $this->log('Recognized potential ' . $this->name . ' (delivery message).');
108
109
        if (!parent::preProcessPending()) {
110
            return false;
111
        };
112
    }
113
114
    /**
115
     * Verifies & saves data for one confirm request.
116
     *
117
     * Returns "false" if the process should be canceled. Otherwise "true".
118
     *
119
     * @param array $processOptions please include 'saveKey',
120
     *                              'saveKeyReceiptType' and
121
     *                              'saveKeyReceiptTypeLargest'
122
     *
123
     * @return bool
124
     */
125
    protected function processConfirmRequest($confirmRequest, array $processOptions = [])
126
    {
127
        if (!parent::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...
128
            return false;
129
        }
130
131
        /** @var bool $success */
132
        $success = false;
133
134
        // go through each message ID and try to save data
135
        foreach ($this->ackedMsgIds as $ackedMsgId) {
136
            $ackedMsgId = $this->getCryptTool()->bin2hex($ackedMsgId);
137
138
            // check whether we are requested to handle this message
139
            if (!$this->getCryptTool()->stringCompare($confirmRequest['extra_data'], $ackedMsgId)) {
140
                continue;
141
            }
142
143
            $this->log('Found acknowledged message ID ' . $ackedMsgId . ' of pending request in delivery message.');
144
145
            // save data
146
            try {
147
                $this->setDataForRequest($confirmRequest, [
148
                    $processOptions['saveKey'] => $ackedMsgId,
149
                    $processOptions['saveKeyReceiptType'] => $this->receiptType
150
                    // saveKeyReceiptTypeLargest is set by preSaveData() as it needs to
151
                    // analyse the old data
152
                ], $processOptions);
153
            } catch (Exception $e) {
154
                $this->log('Could not save data for request.', $e->getMessage());
155
            }
156
157
            // whether the code is the same as the requested one is verified in
158
            // the actual 2FA provider (verifyFromInput) later
159
160
            $success = true;
161
        }
162
163
        if (!$success) {
164
            $this->log('It turned out the message actually seems to be a delivery message unrelated to this 2FA mode.');
165
        }
166
167
        return $success;
168
    }
169
170
    /**
171
     * Checks whether the previously saved receipt type is smaller than the
172
     * one got currently.
173
     *
174
     * @param array $confirmRequest  the confirm request
175
     * @param array $oldProviderData old data read
176
     * @param array $setData         new data to set
177
     * @param array $processOptions  custom options (optional)
178
     *
179
     * @throws XenForo_Exception
180
     * @return bool
181
     */
182
    protected function preSaveData(array $confirmRequest, array &$oldProviderData, array &$setData, array $processOptions = [])
183
    {
184
        // save largest delivery receipt
185
        if ($processOptions['saveKeyReceiptTypeLargest']) {
186
            // if current receipt is smaller than all previous ones
187
            if (!isset($oldProviderData[$processOptions['saveKeyReceiptTypeLargest']]) ||
188
                $oldProviderData[$processOptions['saveKeyReceiptTypeLargest']] < $this->receiptType
189
            ) {
190
                $setData[$processOptions['saveKeyReceiptTypeLargest']] = $this->receiptType;
191
            }
192
        }
193
194
        return true;
195
    }
196
    /**
197
     * Calls 2FA provider verification if it could do some blocking.
198
     *
199
     * @param array $confirmRequest the confirm request
200
     * @param array $providerData   merged provider data
201
     * @param array $processOptions custom options (optional)
202
     *
203
     * @throws XenForo_Exception
204
     * @return bool
205
     */
206
    protected function preSaveDataMerged(array $confirmRequest, array &$providerData, array $processOptions = [])
207
    {
208
        // if it is a decline message, let it check by the 2FA provider to
209
        // potentially ban or block users
210
        if ($processOptions['tfaProviderCallbackOnDecline'] && $this->receiptType === 4) {
211
            $this->log('Detected decline receipt type. Calling 2FA provider now to execute potential banning actions.');
212
213
            /** @var ThreemaGateway_Tfa_AbstractProvider $tfaProvider */
214
            $tfaProvider = new $processOptions['tfaProviderCallbackOnDecline']($processOptions['tfaProviderId']);
215
216
            // call original verification
217
            if ($tfaProvider->verifyFromInput(
218
                'callback',
219
                $this->callback->getInput(),
220
                $this->getUserData($confirmRequest['user_id']),
221
                $providerData
222
            )) {
223
                // it is unexpected to succeed (because of the delivery receipt), so if this happens we need to log this
224
                $this->log('Unexpected error: For some strange reason, the callback verification succeeded.');
225
            }
226
        }
227
228
        return true;
229
    }
230
}
231