Completed
Push — master ( ad9fa0...256f5f )
by Michal
16s
created

EventsController   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 23
c 1
b 0
f 0
lcom 2
cbo 7
dl 0
loc 186
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A initialize() 0 7 1
A beforeFilter() 0 4 1
B index() 0 57 7
B _validateRequest() 0 59 8
A _getHash() 0 9 2
A _getAppropriateStatus() 0 18 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
    public function initialize()
34
    {
35
        parent::initialize();
36
        $this->loadComponent('Csrf');
37
38
        $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
    }
40
41
    public function beforeFilter(Event $event)
42
    {
43
        $this->eventManager()->off($this->Csrf);
44
    }
45
46
    public function index()
47
    {
48
        // Only allow POST requests
49
        $this->request->allowMethod(['post']);
50
51
        // Validate request
52
        if (($statusCode = $this->_validateRequest($this->request)) !== 201) {
53
            Log::error(
54
                'Could not validate the request. Sending a '
55
                    . $statusCode . ' response.'
56
            );
57
58
            // Send a response
59
            $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
            $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
            return $this->response;
63
        }
64
65
        $issuesData = $this->request->input('json_decode', true);
66
        $eventAction = $issuesData['action'];
67
        $issueNumber = $issuesData['issue'] ? $issuesData['issue']['number'] : '';
68
69
        if ($eventAction === 'closed'
70
            || $eventAction === 'opened'
71
            || $eventAction === 'reopened'
72
        ) {
73
            $status = $this->_getAppropriateStatus($eventAction);
74
75
            if (($reportsUpdated = $this->Reports->setLinkedReportStatus($issueNumber, $status)) > 0) {
76
                Log::debug(
77
                    $reportsUpdated . ' linked reports to issue number '
78
                        . $issueNumber . ' were updated according to recieved action '
79
                        . $eventAction
80
                );
81
            } else {
82
                Log::info(
83
                    'No linked report found for issue number \'' . $issueNumber
84
                    . '\'. Ignoring the event.'
85
                );
86
                $statusCode = 204;
87
                print_r('asd ' . $issueNumber . ' ' . $eventAction);
88
            }
89
        } else {
90
            Log::info(
91
                'Recieved a webhook event for action \'' . $eventAction
92
                . '\' on issue number ' . $issueNumber . '. Ignoring the event.'
93
            );
94
            $statusCode = 204;
95
        }
96
97
        // Send a response
98
        $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
        $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
        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
    protected function _validateRequest($request)
113
    {
114
        // Default $statusCode
115
        $statusCode = 201;
116
117
        $userAgent = $request->getHeaderLine('User-Agent');
118
        $eventType = $request->getHeaderLine('X-GitHub-Event');
119
120
        $recievedHashHeader = $request->getHeaderLine('X-Hub-Signature');
121
        $algo = '';
122
        $recievedHash = '';
123
        if ($recievedHashHeader !== NULL) {
124
            $parts = explode('=', $recievedHashHeader);
125
            if (count($parts) > 1) {
126
                $algo = $parts[0];
127
                $recievedHash = $parts[1];
128
            }
129
        }
130
131
        $expectedHash = $this->_getHash(file_get_contents('php://input'), $algo);
132
133
        if ($userAgent !== NULL && strpos($userAgent, 'GitHub-Hookshot') !== 0) {
134
            // Check if the User-agent is Github
135
            // Otherwise, Send a '403: Forbidden'
136
137
            Log::error(
138
                'Invalid User agent: ' . $userAgent
139
                . '. Ignoring the event.'
140
            );
141
            $statusCode = 403;
142
143
            return $statusCode;
144
        } elseif ($eventType !== NULL && $eventType !== 'issues') {
145
            // Check if the request is based on 'issues' event
146
            // Otherwise, Send a '400: Bad Request'
147
148
            Log::error(
149
                'Unexpected event type: ' . $eventType
150
                . '. Ignoring the event.'
151
            );
152
            $statusCode = 400;
153
154
            return $statusCode;
155
        } elseif ($recievedHash !== $expectedHash) {
156
            // Check if hash matches
157
            // Otherwise, Send a '401: Unauthorized'
158
159
            Log::error(
160
                'Recieved hash ' . $recievedHash . ' does not match '
161
                . ' expected hash ' . $expectedHash
162
                . '. Ignoring the event.'
163
            );
164
            $statusCode = 401;
165
166
            return $statusCode;
167
        }
168
169
        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
    protected function _getHash($payload, $algo)
181
    {
182
        if ($algo === '') {
183
            return '';
184
        }
185
        $key = Configure::read('GithubWebhookSecret');
186
187
        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
    protected function _getAppropriateStatus($action)
198
    {
199
        $status = 'forwarded';
200
201
        switch ($action) {
202
            case 'opened':
203
                break;
204
205
            case 'reopened':
206
                break;
207
208
            case 'closed':
209
                $status = 'resolved';
210
                break;
211
        }
212
213
        return $status;
214
    }
215
}
216