Completed
Push — master ( bc825a...1aba9a )
by William
06:03
created

EventsController::_validateRequest()   B

Complexity

Conditions 10
Paths 15

Size

Total Lines 68
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 37
nc 15
nop 1
dl 0
loc 68
ccs 38
cts 38
cp 1
crap 10
rs 7.6666
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/* vim: set expandtab sw=4 ts=4 sts=4: */
3
4
/**
5
 * Events controller Github webhook events
6
 *
7
 * phpMyAdmin Error reporting server
8
 * Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
9
 *
10
 * Licensed under The MIT License
11
 * For full copyright and license information, please see the LICENSE.txt
12
 * Redistributions of files must retain the above copyright notice.
13
 *
14
 * @copyright Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
15
 * @license   https://opensource.org/licenses/mit-license.php MIT License
16
 *
17
 * @see      https://www.phpmyadmin.net/
18
 */
19
20
namespace App\Controller;
21
22
use Cake\Core\Configure;
23
use Cake\Event\Event;
24
use Cake\Log\Log;
25
use Cake\ORM\TableRegistry;
26
27
/**
28
 * Events controller Github webhook events
29
 */
30
class EventsController extends AppController
31
{
32
33 1
    public function initialize()
34
    {
35 1
        parent::initialize();
36 1
        $this->loadComponent('Csrf');
37
38 1
        $this->Reports = TableRegistry::get('Reports');
0 ignored issues
show
Deprecated Code introduced by
The function Cake\ORM\TableRegistry::get() has been deprecated: 3.6.0 Use \Cake\ORM\Locator\TableLocator::get() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

38
        $this->Reports = /** @scrutinizer ignore-deprecated */ TableRegistry::get('Reports');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Bug Best Practice introduced by
The property Reports does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
39 1
    }
40
41 1
    public function beforeFilter(Event $event)
42
    {
43 1
        $this->eventManager()->off($this->Csrf);
0 ignored issues
show
Deprecated Code introduced by
The function Cake\Controller\Controller::eventManager() has been deprecated: 3.5.0 Use getEventManager()/setEventManager() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

43
        /** @scrutinizer ignore-deprecated */ $this->eventManager()->off($this->Csrf);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
44 1
    }
45
46 1
    public function index()
47
    {
48
        // Only allow POST requests
49 1
        $this->request->allowMethod(['post']);
50
51
        // Validate request
52 1
        if (($statusCode = $this->_validateRequest($this->request)) !== 201) {
0 ignored issues
show
Bug introduced by
$this->request of type Cake\Http\ServerRequest is incompatible with the type Cake\Http\Client\Request expected by parameter $request of App\Controller\EventsCon...ler::_validateRequest(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

52
        if (($statusCode = $this->_validateRequest(/** @scrutinizer ignore-type */ $this->request)) !== 201) {
Loading history...
53 1
            Log::error(
54
                'Could not validate the request. Sending a '
55 1
                    . $statusCode . ' response.'
56
            );
57
58
            // Send a response
59 1
            $this->auto_render = false;
0 ignored issues
show
Bug Best Practice introduced by
The property auto_render does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
60 1
            $this->response->statusCode($statusCode);
0 ignored issues
show
Deprecated Code introduced by
The function Cake\Http\Response::statusCode() has been deprecated: 3.4.0 Use `getStatusCode()` and `withStatus()` instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

60
            /** @scrutinizer ignore-deprecated */ $this->response->statusCode($statusCode);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
61
62 1
            return $this->response;
63 1
        } elseif ($statusCode === 200) {
64
           // Send a success response to ping event
65
            $this->auto_render = false;
66
            $this->response->statusCode($statusCode);
0 ignored issues
show
Deprecated Code introduced by
The function Cake\Http\Response::statusCode() has been deprecated: 3.4.0 Use `getStatusCode()` and `withStatus()` instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

66
            /** @scrutinizer ignore-deprecated */ $this->response->statusCode($statusCode);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
67
68
            return $this->response;
69
        }
70
71 1
        $issuesData = $this->request->input('json_decode', true);
72 1
        $eventAction = $issuesData['action'];
73 1
        $issueNumber = $issuesData['issue'] ? $issuesData['issue']['number'] : '';
74
75 1
        if ($eventAction === 'closed'
76 1
            || $eventAction === 'opened'
77 1
            || $eventAction === 'reopened'
78
        ) {
79 1
            $status = $this->_getAppropriateStatus($eventAction);
80
81 1
            if (($reportsUpdated = $this->Reports->setLinkedReportStatus($issueNumber, $status)) > 0) {
82 1
                Log::debug(
83 1
                    $reportsUpdated . ' linked reports to issue number '
84 1
                        . $issueNumber . ' were updated according to received action '
85 1
                        . $eventAction
86
                );
87
            } else {
88 1
                Log::info(
89 1
                    'No linked report found for issue number \'' . $issueNumber
90 1
                    . '\'. Ignoring the event.'
91
                );
92 1
                $statusCode = 204;
93
            }
94
        } else {
95 1
            Log::info(
96 1
                'received a webhook event for action \'' . $eventAction
97 1
                . '\' on issue number ' . $issueNumber . '. Ignoring the event.'
98
            );
99 1
            $statusCode = 204;
100
        }
101
102
        // Send a response
103 1
        $this->auto_render = false;
104 1
        $this->response->statusCode($statusCode);
0 ignored issues
show
Deprecated Code introduced by
The function Cake\Http\Response::statusCode() has been deprecated: 3.4.0 Use `getStatusCode()` and `withStatus()` instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

104
        /** @scrutinizer ignore-deprecated */ $this->response->statusCode($statusCode);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
105
106 1
        return $this->response;
107
    }
108
109
110
    /**
111
     * Validate HTTP Request received
112
     *
113
     * @param \Cake\Http\Client\Request $request Request object
114
     *
115
     * @return int status code based on if this is a valid request
116
     */
117 1
    protected function _validateRequest($request)
118
    {
119
        // Default $statusCode
120 1
        $statusCode = 201;
121
122 1
        $userAgent = $request->getHeaderLine('User-Agent');
123 1
        $eventType = $request->getHeaderLine('X-GitHub-Event');
124
125 1
        $receivedHashHeader = $request->getHeaderLine('X-Hub-Signature');
126 1
        $algo = '';
127 1
        $receivedHash = '';
128 1
        if ($receivedHashHeader !== null) {
0 ignored issues
show
introduced by
The condition $receivedHashHeader !== null is always true.
Loading history...
129 1
            $parts = explode('=', $receivedHashHeader);
130 1
            if (count($parts) > 1) {
131 1
                $algo = $parts[0];
132 1
                $receivedHash = $parts[1];
133
            }
134
        }
135
136 1
        $expectedHash = $this->_getHash(file_get_contents('php://input'), $algo);
137
138 1
        if ($userAgent !== null && strpos($userAgent, 'GitHub-Hookshot') !== 0) {
139
            // Check if the User-agent is Github
140
            // Otherwise, Send a '403: Forbidden'
141
142 1
            Log::error(
143 1
                'Invalid User agent: ' . $userAgent
144 1
                . '. Ignoring the event.'
145
            );
146 1
            $statusCode = 403;
147
148 1
            return $statusCode;
149 1
        } elseif ($eventType !== null && $eventType === 'ping') {
150
            // Check if the request is based on 'issues' event
151
            // Otherwise, Send a '400: Bad Request'
152
153 1
            Log::info(
154 1
                'Ping event type received.'
155
            );
156 1
            $statusCode = 200;
157
158 1
            return $statusCode;
159 1
        } elseif ($eventType !== null && $eventType !== 'issues') {
160
            // Check if the request is based on 'issues' event
161
            // Otherwise, Send a '400: Bad Request'
162
163 1
            Log::error(
164 1
                'Unexpected event type: ' . $eventType
165 1
                . '. Ignoring the event.'
166
            );
167 1
            $statusCode = 400;
168
169 1
            return $statusCode;
170 1
        } elseif ($receivedHash !== $expectedHash) {
171
            // Check if hash matches
172
            // Otherwise, Send a '401: Unauthorized'
173
174 1
            Log::error(
175 1
                'received hash ' . $receivedHash . ' does not match '
176 1
                . ' expected hash ' . $expectedHash
177 1
                . '. Ignoring the event.'
178
            );
179 1
            $statusCode = 401;
180
181 1
            return $statusCode;
182
        }
183
184 1
        return $statusCode;
185
    }
186
187
    /**
188
     * Get the hash of raw POST payload
189
     *
190
     * @param string $payload Raw POST body string
191
     * @param string $algo    Algorithm used to calculate the hash
192
     *
193
     * @return string Hmac Digest-based hash of payload
194
     */
195 1
    protected function _getHash($payload, $algo)
196
    {
197 1
        if ($algo === '') {
198 1
            return '';
199
        }
200 1
        $key = Configure::read('GithubWebhookSecret');
201
202 1
        return hash_hmac($algo, $payload, $key);
203
    }
204
205
    /**
206
     * Get appropriate new status based on action received in github event
207
     *
208
     * @param string $action Action received in Github webhook event
209
     *
210
     * @return string Appropriate new status for the related reports
211
     */
212 1
    protected function _getAppropriateStatus($action)
213
    {
214 1
        $status = 'forwarded';
215
216
        switch ($action) {
217 1
            case 'opened':
218 1
                break;
219
220 1
            case 'reopened':
221 1
                break;
222
223 1
            case 'closed':
224 1
                $status = 'resolved';
225 1
                break;
226
        }
227
228 1
        return $status;
229
    }
230
}
231