Completed
Pull Request — master (#155)
by Deven
02:37
created

EventsController::_getAppropriateStatus()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
ccs 10
cts 10
cp 1
rs 9.2
cc 4
eloc 11
nc 4
nop 1
crap 4
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 1
                print_r('asd ' . $issueNumber . ' ' . $eventAction);
88
            }
89
        } else {
90 1
            Log::info(
91 1
                'Recieved a webhook event for action \'' . $eventAction
92 1
                . '\' on issue number ' . $issueNumber . '. Ignoring the event.'
93
            );
94 1
            $statusCode = 204;
95
        }
96
97
        // Send a response
98 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...
99 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...
100
101 1
        return $this->response;
102
    }
103
104
105
    /**
106
     * Validate HTTP Request recieved
107
     *
108
     * @param Request $request Request object
109
     *
110
     * @return int status code based on if this is a valid request
111
     */
112 1
    protected function _validateRequest($request)
113
    {
114
        // Default $statusCode
115 1
        $statusCode = 201;
116
117 1
        $userAgent = $request->getHeaderLine('User-Agent');
118 1
        $eventType = $request->getHeaderLine('X-GitHub-Event');
119
120 1
        $recievedHashHeader = $request->getHeaderLine('X-Hub-Signature');
121 1
        $algo = '';
122 1
        $recievedHash = '';
123 1
        if ($recievedHashHeader !== NULL) {
124 1
            $parts = explode('=', $recievedHashHeader);
125 1
            if (count($parts) > 1) {
126 1
                $algo = $parts[0];
127 1
                $recievedHash = $parts[1];
128
            }
129
        }
130
131 1
        $expectedHash = $this->_getHash(file_get_contents('php://input'), $algo);
132
133 1
        if ($userAgent !== NULL && strpos($userAgent, 'GitHub-Hookshot') !== 0) {
134
            // Check if the User-agent is Github
135
            // Otherwise, Send a '403: Forbidden'
136
137 1
            Log::error(
138 1
                'Invalid User agent: ' . $userAgent
139 1
                . '. Ignoring the event.'
140
            );
141 1
            $statusCode = 403;
142
143 1
            return $statusCode;
144 1
        } elseif ($eventType !== NULL && $eventType !== 'issues') {
145
            // Check if the request is based on 'issues' event
146
            // Otherwise, Send a '400: Bad Request'
147
148 1
            Log::error(
149 1
                'Unexpected event type: ' . $eventType
150 1
                . '. Ignoring the event.'
151
            );
152 1
            $statusCode = 400;
153
154 1
            return $statusCode;
155 1
        } elseif ($recievedHash !== $expectedHash) {
156
            // Check if hash matches
157
            // Otherwise, Send a '401: Unauthorized'
158
159 1
            Log::error(
160 1
                'Recieved hash ' . $recievedHash . ' does not match '
161 1
                . ' expected hash ' . $expectedHash
162 1
                . '. Ignoring the event.'
163
            );
164 1
            $statusCode = 401;
165
166 1
            return $statusCode;
167
        }
168
169 1
        return $statusCode;
170
    }
171
172
    /**
173
     * Get the hash of raw POST payload
174
     *
175
     * @param string $payload Raw POST body string
176
     * @param string $algo    Algorithm used to calculate the hash
177
     *
178
     * @return string Hmac Digest-based hash of payload
179
     */
180 1
    protected function _getHash($payload, $algo)
181
    {
182 1
        if ($algo === '') {
183 1
            return '';
184
        }
185 1
        $key = Configure::read('GithubWebhookSecret');
186
187 1
        return hash_hmac($algo, $payload, $key);
188
    }
189
190
    /**
191
     * Get appropriate new status based on action recieved in github event
192
     *
193
     * @param string $action Action recieved in Github webhook event
194
     *
195
     * @return string Appropriate new status for the related reports
196
     */
197 1
    protected function _getAppropriateStatus($action)
198
    {
199 1
        $status = 'forwarded';
200
201
        switch ($action) {
202 1
            case 'opened':
203 1
                break;
204
205 1
            case 'reopened':
206 1
                break;
207
208 1
            case 'closed':
209 1
                $status = 'resolved';
210 1
                break;
211
        }
212
213 1
        return $status;
214
    }
215
}
216