Completed
Push — master ( 62e12a...79aede )
by Sebastien
02:14
created

Webhook::getDefaultEventNames()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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