Completed
Push — master ( 7b141a...d4dccf )
by Sebastien
02:59
created

Webhook::getEventClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4286
cc 1
eloc 3
nc 1
nop 1
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 getEventMap()
65
    {
66
        if (empty($this->eventMap)) {
67
            $this->eventMap = array();
68
69
            $events = array(
70
              'commit_comment',
71
              'create',
72
              'delete',
73
              'deployment',
74
              'deployment_status',
75
              'download',
76
              'follow',
77
              'fork',
78
              'fork_apply',
79
              'gist',
80
              'gollum',
81
              'issue_comment',
82
              'issues',
83
              'member',
84
              'membership',
85
              'page_build',
86
              'public',
87
              'ping',
88
              'pull_request',
89
              'pull_request_review_comment',
90
              'push',
91
              'release',
92
              'repository',
93
              'status',
94
              'team_add',
95
              'watch',
96
            );
97
98
            $namespace = '\\Smalot\\Github\\Webhook\\Event\\';
99
100
            foreach ($events as $event) {
101
                $className = str_replace(' ', '', ucwords(str_replace('_', ' ', $event)));
102
                $this->eventMap[$event] = $namespace.$className.'Event';
103
            }
104
        }
105
106
        return $this->eventMap;
107
    }
108
109
    /**
110
     * @param Request $request
111
     * @param string $secret
112
     * @param bool $dispatch
113
     * @return EventBase
114
     *
115
     * @throws \InvalidArgumentException
116
     */
117
    public function parseRequest(Request $request, $secret, $dispatch = true)
118
    {
119
        if (!$this->checkSecurity($request, $secret)) {
120
            throw new \InvalidArgumentException('Invalid security checksum header.');
121
        }
122
123
        if ($className = $this->getEventClassName($this->eventName)) {
124
            $event = new $className($this->eventName, $this->payload, $this->delivery);
125
        } else {
126
            throw new \InvalidArgumentException('Unknown event type.');
127
        }
128
129
        if (null !== $this->eventDispatcher && $dispatch) {
130
            $this->eventDispatcher->dispatch(Events::WEBHOOK_REQUEST, $event);
131
        }
132
133
        return $event;
134
    }
135
136
    /**
137
     * @param Request $request
138
     * @param string $secret
139
     * @return bool
140
     *
141
     * @throws \InvalidArgumentException
142
     */
143
    protected function checkSecurity(Request $request, $secret)
144
    {
145
        // Reset any previously payload set.
146
        $this->eventName = null;
147
        $this->payload = null;
148
        $this->delivery = null;
149
150
        // Extract Github headers from request.
151
        $signature = $request->headers->get('X-Hub-Signature');
152
        $event = $request->headers->get('X-Github-Event');
153
        $delivery = $request->headers->get('X-Github-Delivery');
154
        $payload = $request->getContent();
155
156
        if (!isset($signature, $event, $delivery)) {
157
            throw new \InvalidArgumentException('Missing Github headers.');
158
        }
159
160
        if ($this->validateSignature($secret, $signature, $payload)) {
161
            $this->eventName = $event;
162
            $this->payload = $payload;
163
            $this->delivery = $delivery;
164
165
            return true;
166
        }
167
168
        return false;
169
    }
170
171
    /**
172
     * @param string $secret
173
     * @param string $signatureHeader
174
     * @param string $payload
175
     * @return bool
176
     */
177
    protected function validateSignature($secret, $signatureHeader, $payload)
178
    {
179
        list ($algo, $gitHubSignature) = explode('=', $signatureHeader);
180
        $payloadHash = hash_hmac($algo, $payload, $secret);
181
182
        return ($payloadHash == $gitHubSignature);
183
    }
184
}
185