Completed
Push — master ( 667317...f33c3d )
by William
09:54
created

EventsController::index()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 8.0402

Importance

Changes 0
Metric Value
dl 0
loc 62
ccs 32
cts 35
cp 0.9143
rs 7.5846
c 0
b 0
f 0
cc 8
nc 8
nop 0
crap 8.0402

How to fix   Long Method   

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
Documentation introduced by
The property Reports does not exist on object<App\Controller\EventsController>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Deprecated Code introduced by
The method Cake\ORM\TableRegistry::get() has been deprecated with message: 3.6.0 Use \Cake\ORM\Locator\TableLocator::get() instead.

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

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

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 method Cake\Event\EventDispatcherTrait::eventManager() has been deprecated with message: 3.5.0 Use getEventManager()/setEventManager() instead.

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

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class 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) {
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
Documentation introduced by
The property auto_render does not exist on object<App\Controller\EventsController>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
60 1
            $this->response->statusCode($statusCode);
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Http\Response::statusCode() has been deprecated with message: 3.4.0 Use `getStatusCode()` and `withStatus()` instead.

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

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class 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;
0 ignored issues
show
Documentation introduced by
The property auto_render does not exist on object<App\Controller\EventsController>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
66
            $this->response->statusCode($statusCode);
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Http\Response::statusCode() has been deprecated with message: 3.4.0 Use `getStatusCode()` and `withStatus()` instead.

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

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class 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;
0 ignored issues
show
Documentation introduced by
The property auto_render does not exist on object<App\Controller\EventsController>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
104 1
        $this->response->statusCode($statusCode);
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Http\Response::statusCode() has been deprecated with message: 3.4.0 Use `getStatusCode()` and `withStatus()` instead.

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

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class 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) {
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 1
        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