Completed
Pull Request — master (#3)
by Romain
01:47
created

Webhook::isValidHubSignature()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
1
<?php
2
namespace Kerox\Messenger\Api;
3
4
use GuzzleHttp\Psr7\ServerRequest;
5
use Kerox\Messenger\Helper\XHubSignatureHelper;
6
use Kerox\Messenger\Model\Callback\Entry;
7
use Kerox\Messenger\Request\WebhookRequest;
8
use Kerox\Messenger\Response\WebhookResponse;
9
10
class Webhook extends AbstractApi
11
{
12
13
    /**
14
     * @var string
15
     */
16
    protected $appSecret;
17
18
    /**
19
     * @var string
20
     */
21
    protected $verifyToken;
22
23
    /**
24
     * @var \Psr\Http\Message\ServerRequestInterface
25
     */
26
    protected $request;
27
28
    /**
29
     * @var array
30
     */
31
    protected $decodedBody;
32
33
    /**
34
     * @var \Kerox\Messenger\Model\Entry[]
35
     */
36
    protected $hydratedEntries;
37
38
    /**
39
     * Webhook constructor.
40
     *
41
     * @param string $appSecret
42
     * @param string $verifyToken
43
     * @param string $pageToken
44
     */
45
    public function __construct(string $appSecret, string $verifyToken, string $pageToken)
46
    {
47
        parent::__construct($pageToken);
48
49
        $this->appSecret = $appSecret;
50
        $this->verifyToken = $verifyToken;
51
        $this->request = ServerRequest::fromGlobals();
52
    }
53
54
    /**
55
     * @return bool
56
     */
57
    public function isValidToken(): bool
58
    {
59
        if ($this->request->getMethod() !== 'GET') {
60
            return false;
61
        }
62
63
        $params = $this->request->getQueryParams();
64
        if (!isset($params['hub_verify_token'])) {
65
            return false;
66
        }
67
68
        return ($params['hub_mode'] === 'subscribe' && $params['hub_verify_token'] === $this->verifyToken);
69
    }
70
71
    /**
72
     * @return string|null
73
     */
74
    public function getChallenge()
75
    {
76
        $params = $this->request->getQueryParams();
77
78
        return $params['hub_challenge'] ?? null;
79
    }
80
81
    /**
82
     * @return \Kerox\Messenger\Response\WebhookResponse
83
     */
84
    public function sendSubscribe(): WebhookResponse
85
    {
86
        $request = new WebhookRequest($this->pageToken);
87
        $response = $this->client->post('/me/subscribed_apps', $request->build());
88
89
        return new WebhookResponse($response);
90
    }
91
92
    /**
93
     * @return bool
94
     */
95
    public function isValidCallback(): bool
96
    {
97
        if ($this->isValidHubSignature()) {
98
            return false;
99
        }
100
101
        $decodedBody = $this->getDecodedBody();
102
103
        $object = $decodedBody['object'] ?? null;
104
        $entry = $decodedBody['entry'] ?? null;
105
106
        return ($object === 'page' && $entry !== null);
107
    }
108
109
    /**
110
     * @return array
111
     * @throws \Exception
112
     */
113
    public function getDecodedBody(): array
114
    {
115
        if (is_null($this->decodedBody)) {
116
            $decodedBody = json_decode($this->request->getBody(), true);
117
            if (json_last_error() !== JSON_ERROR_NONE) {
118
                throw new \Exception('Error while parsing the request body');
119
            }
120
121
            $this->decodedBody = $decodedBody;
122
        }
123
124
        return $this->decodedBody;
125
    }
126
127
    /**
128
     * @return \Kerox\Messenger\Model\Entry[]
129
     */
130
    public function getCallbackEntries(): array
131
    {
132
        return $this->getHydratedEntries();
133
    }
134
135
    /**
136
     * @return array
137
     */
138
    public function getCallbackEvents(): array
139
    {
140
        $events = [];
141
        foreach ($this->getHydratedEntries() as $hydratedEntry) {
142
            /** @var \Kerox\Messenger\Model\Callback\Entry $hydratedEntry */
143
            $events[] += $hydratedEntry->getEvents();
144
        }
145
146
        return $events;
147
    }
148
149
    /**
150
     * @return array|\Kerox\Messenger\Model\Callback\Entry[]
151
     */
152
    private function getHydratedEntries(): array
153
    {
154
        if (is_null($this->hydratedEntries)) {
155
            $decodedBody = $this->getDecodedBody();
156
157
            $hydrated = [];
158
            foreach ($decodedBody['entry'] as $entry) {
159
                $hydrated[] = Entry::create($entry);
160
            }
161
162
            $this->hydratedEntries = $hydrated;
163
        }
164
165
        return $this->hydratedEntries;
166
    }
167
168
    /**
169
     * @return bool
170
     */
171
    private function isValidHubSignature(): bool
172
    {
173
        $headers = $this->request->getHeader('X-Hub-Signature');
174
        $content = $this->request->getBody();
175
176
        if (empty($headers)) {
177
            return false;
178
        }
179
180
        return XHubSignatureHelper::validate($content, $this->appSecret, $headers[0]);
181
    }
182
}
183