Completed
Push — master ( b460f9...048e1d )
by Sebastien
02:21
created

Webhook::getEventMap()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 14
rs 9.4286
cc 3
eloc 8
nc 2
nop 0
1
<?php
2
3
namespace Smalot\Github\Webhook;
4
5
use Smalot\Github\Webhook\Event\EventBase;
6
use Symfony\Component\EventDispatcher\EventDispatcher;
7
use Symfony\Component\HttpFoundation\Request;
8
9
/**
10
 * Class Webhook
11
 * @package Smalot\Github\Webhook
12
 */
13
class Webhook
14
{
15
    /**
16
     * @var EventDispatcher
17
     */
18
    protected $eventDispatcher;
19
20
    /**
21
     * @var string
22
     */
23
    protected $eventName;
24
25
    /**
26
     * @var string
27
     */
28
    protected $payload;
29
30
    /**
31
     * @var string
32
     */
33
    protected $delivery;
34
35
    /**
36
     * @var array
37
     */
38
    protected $eventMap;
39
40
    /**
41
     * Webhook constructor.
42
     * @param EventDispatcher $eventDispatcher
43
     */
44
    public function __construct(EventDispatcher $eventDispatcher = null)
45
    {
46
        $this->eventDispatcher = $eventDispatcher;
47
        $this->eventMap = array();
48
    }
49
50
    /**
51
     * @param string $event
52
     * @return string
53
     */
54
    public function getEventClassName($event)
55
    {
56
        $map = $this->getEventMap();
57
58
        return $map[$event];
59
    }
60
61
    /**
62
     * @return array
63
     */
64
    public function getDefaultEventNames()
65
    {
66
        return array(
67
            'commit_comment',
68
            'create',
69
            'delete',
70
            'deployment',
71
            'deployment_status',
72
            'download',
73
            'follow',
74
            'fork',
75
            'fork_apply',
76
            'gist',
77
            'gollum',
78
            'issue_comment',
79
            'issues',
80
            'member',
81
            'membership',
82
            'page_build',
83
            'public',
84
            'ping',
85
            'pull_request',
86
            'pull_request_review_comment',
87
            'push',
88
            'release',
89
            'repository',
90
            'status',
91
            'team_add',
92
            'watch',
93
        );
94
    }
95
96
    /**
97
     * @return array
98
     */
99
    public function getEventMap()
100
    {
101
        if (empty($this->eventMap)) {
102
            $this->eventMap = array();
103
            $namespace = '\\Smalot\\Github\\Webhook\\Event\\';
104
105
            foreach ($this->getDefaultEventNames() as $event) {
106
                $className = str_replace(' ', '', ucwords(str_replace('_', ' ', $event)));
107
                $this->eventMap[$event] = $namespace . $className . 'Event';
108
            }
109
        }
110
111
        return $this->eventMap;
112
    }
113
114
    /**
115
     * @param Request $request
116
     * @param string $secret
117
     * @param bool $dispatch
118
     * @return EventBase
119
     *
120
     * @throws \InvalidArgumentException
121
     */
122
    public function parseRequest(Request $request, $secret, $dispatch = true)
123
    {
124
        if (!$this->checkSecurity($request, $secret)) {
125
            throw new \InvalidArgumentException('Invalid security checksum header.');
126
        }
127
128
        if ($className = $this->getEventClassName($this->eventName)) {
129
            $event = new $className($this->eventName, $this->payload, $this->delivery);
130
        } else {
131
            throw new \InvalidArgumentException('Unknown event type.');
132
        }
133
134
        if (null !== $this->eventDispatcher && $dispatch) {
135
            $this->eventDispatcher->dispatch(Events::WEBHOOK_REQUEST, $event);
136
        }
137
138
        return $event;
139
    }
140
141
    /**
142
     * @param Request $request
143
     * @param string $secret
144
     * @return bool
145
     *
146
     * @throws \InvalidArgumentException
147
     */
148
    protected function checkSecurity(Request $request, $secret)
149
    {
150
        // Reset any previously payload set.
151
        $this->eventName = null;
152
        $this->payload = null;
153
        $this->delivery = null;
154
155
        // Extract Github headers from request.
156
        $signature = (string) $request->headers->get('X-Hub-Signature');
157
        $event = (string) $request->headers->get('X-Github-Event');
158
        $delivery = (string) $request->headers->get('X-Github-Delivery');
159
        $payload = (string) $request->getContent();
160
161
        if (!isset($signature, $event, $delivery)) {
162
            throw new \InvalidArgumentException('Missing Github headers.');
163
        }
164
165
        if ($this->validateSignature($secret, $signature, $payload)) {
166
            $this->eventName = $event;
167
            $this->payload = $payload;
168
            $this->delivery = $delivery;
169
170
            return true;
171
        }
172
173
        return false;
174
    }
175
176
    /**
177
     * @param string $secret
178
     * @param string $signatureHeader
179
     * @param string $payload
180
     * @return bool
181
     */
182
    protected function validateSignature($secret, $signatureHeader, $payload)
183
    {
184
        list ($algo, $gitHubSignature) = explode('=', $signatureHeader);
185
        $payloadHash = hash_hmac($algo, $payload, $secret);
186
187
        return ($payloadHash == $gitHubSignature);
188
    }
189
}
190