Completed
Push — master ( 256f5f...384bea )
by Deven
03:54
created

EventsController::index()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 56
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 7

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 56
ccs 31
cts 31
cp 1
rs 7.7489
cc 7
eloc 34
nc 7
nop 0
crap 7

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...
39 1
    }
40
41 1
    public function beforeFilter(Event $event)
42
    {
43 1
        $this->eventManager()->off($this->Csrf);
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
        }
64
65 1
        $issuesData = $this->request->input('json_decode', true);
66 1
        $eventAction = $issuesData['action'];
67 1
        $issueNumber = $issuesData['issue'] ? $issuesData['issue']['number'] : '';
68
69 1
        if ($eventAction === 'closed'
70 1
            || $eventAction === 'opened'
71 1
            || $eventAction === 'reopened'
72
        ) {
73 1
            $status = $this->_getAppropriateStatus($eventAction);
74
75 1
            if (($reportsUpdated = $this->Reports->setLinkedReportStatus($issueNumber, $status)) > 0) {
76 1
                Log::debug(
77 1
                    $reportsUpdated . ' linked reports to issue number '
78 1
                        . $issueNumber . ' were updated according to recieved action '
79 1
                        . $eventAction
80
                );
81
            } else {
82 1
                Log::info(
83 1
                    'No linked report found for issue number \'' . $issueNumber
84 1
                    . '\'. Ignoring the event.'
85
                );
86 1
                $statusCode = 204;
87
            }
88
        } else {
89 1
            Log::info(
90 1
                'Recieved a webhook event for action \'' . $eventAction
91 1
                . '\' on issue number ' . $issueNumber . '. Ignoring the event.'
92
            );
93 1
            $statusCode = 204;
94
        }
95
96
        // Send a response
97 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...
98 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...
99
100 1
        return $this->response;
101
    }
102
103
104
    /**
105
     * Validate HTTP Request recieved
106
     *
107
     * @param Request $request Request object
108
     *
109
     * @return int status code based on if this is a valid request
110
     */
111 1
    protected function _validateRequest($request)
112
    {
113
        // Default $statusCode
114 1
        $statusCode = 201;
115
116 1
        $userAgent = $request->getHeaderLine('User-Agent');
117 1
        $eventType = $request->getHeaderLine('X-GitHub-Event');
118
119 1
        $recievedHashHeader = $request->getHeaderLine('X-Hub-Signature');
120 1
        $algo = '';
121 1
        $recievedHash = '';
122 1
        if ($recievedHashHeader !== NULL) {
123 1
            $parts = explode('=', $recievedHashHeader);
124 1
            if (count($parts) > 1) {
125 1
                $algo = $parts[0];
126 1
                $recievedHash = $parts[1];
127
            }
128
        }
129
130 1
        $expectedHash = $this->_getHash(file_get_contents('php://input'), $algo);
131
132 1
        if ($userAgent !== NULL && strpos($userAgent, 'GitHub-Hookshot') !== 0) {
133
            // Check if the User-agent is Github
134
            // Otherwise, Send a '403: Forbidden'
135
136 1
            Log::error(
137 1
                'Invalid User agent: ' . $userAgent
138 1
                . '. Ignoring the event.'
139
            );
140 1
            $statusCode = 403;
141
142 1
            return $statusCode;
143 1
        } elseif ($eventType !== NULL && $eventType !== 'issues') {
144
            // Check if the request is based on 'issues' event
145
            // Otherwise, Send a '400: Bad Request'
146
147 1
            Log::error(
148 1
                'Unexpected event type: ' . $eventType
149 1
                . '. Ignoring the event.'
150
            );
151 1
            $statusCode = 400;
152
153 1
            return $statusCode;
154 1
        } elseif ($recievedHash !== $expectedHash) {
155
            // Check if hash matches
156
            // Otherwise, Send a '401: Unauthorized'
157
158 1
            Log::error(
159 1
                'Recieved hash ' . $recievedHash . ' does not match '
160 1
                . ' expected hash ' . $expectedHash
161 1
                . '. Ignoring the event.'
162
            );
163 1
            $statusCode = 401;
164
165 1
            return $statusCode;
166
        }
167
168 1
        return $statusCode;
169
    }
170
171
    /**
172
     * Get the hash of raw POST payload
173
     *
174
     * @param string $payload Raw POST body string
175
     * @param string $algo    Algorithm used to calculate the hash
176
     *
177
     * @return string Hmac Digest-based hash of payload
178
     */
179 1
    protected function _getHash($payload, $algo)
180
    {
181 1
        if ($algo === '') {
182 1
            return '';
183
        }
184 1
        $key = Configure::read('GithubWebhookSecret');
185
186 1
        return hash_hmac($algo, $payload, $key);
187
    }
188
189
    /**
190
     * Get appropriate new status based on action recieved in github event
191
     *
192
     * @param string $action Action recieved in Github webhook event
193
     *
194
     * @return string Appropriate new status for the related reports
195
     */
196 1
    protected function _getAppropriateStatus($action)
197
    {
198 1
        $status = 'forwarded';
199
200
        switch ($action) {
201 1
            case 'opened':
202 1
                break;
203
204 1
            case 'reopened':
205 1
                break;
206
207 1
            case 'closed':
208 1
                $status = 'resolved';
209 1
                break;
210
        }
211
212 1
        return $status;
213
    }
214
}
215