EventListener::verifyOrigin()   B
last analyzed

Complexity

Conditions 7
Paths 5

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 36
ccs 17
cts 17
cp 1
rs 8.4106
c 0
b 0
f 0
cc 7
nc 5
nop 0
crap 7
1
<?php
2
3
namespace Botonomous\listener;
4
5
use Botonomous\BotonomousException;
6
use Botonomous\Event;
7
use Botonomous\utility\StringUtility;
8
9
/**
10
 * Class EventListener.
11
 */
12
class EventListener extends AbstractBaseListener
13
{
14
    const KEY = 'event';
15
    const MISSING_TOKEN_OR_APP_ID_MESSAGE = 'Token or api_app_id is not provided';
16
    const MISSING_APP_ID_MESSAGE = 'Api app id must be provided';
17
    const MISSING_VERIFICATION_TOKEN_MESSAGE = 'Verification token must be provided';
18
    const MISSING_EVENT_TYPE_MESSAGE = 'Event type must be specified';
19
20
    private $token;
21
    private $teamId;
22
    private $appId;
23
    private $event;
24
    private $requestEventMaps = [
25
        'ts'       => 'timestamp',
26
        'event_ts' => 'eventTimestamp',
27
    ];
28
29
    /**
30
     * @return mixed
31
     */
32 12
    public function extractRequest()
33
    {
34 12
        return $this->getRequestUtility()->getPostedBody();
35
    }
36
37
    /**
38
     * @return string
39
     */
40 1
    public function getToken(): string
41
    {
42 1
        return $this->token;
43
    }
44
45
    /**
46
     * @param string $token
47
     */
48 1
    public function setToken(string $token)
49
    {
50 1
        $this->token = $token;
51 1
    }
52
53
    /**
54
     * @return string
55
     */
56 1
    public function getTeamId(): string
57
    {
58 1
        return $this->teamId;
59
    }
60
61
    /**
62
     * @param string $teamId
63
     */
64 1
    public function setTeamId(string $teamId)
65
    {
66 1
        $this->teamId = $teamId;
67 1
    }
68
69
    /**
70
     * @return string
71
     */
72 1
    public function getAppId(): string
73
    {
74 1
        return $this->appId;
75
    }
76
77
    /**
78
     * @param string $appId
79
     */
80 1
    public function setAppId(string $appId)
81
    {
82 1
        $this->appId = $appId;
83 1
    }
84
85
    /**
86
     * @throws \Exception
87
     *
88
     * @return Event
89
     */
90 12
    public function getEvent()
91
    {
92 12
        if (!isset($this->event)) {
93 11
            $this->loadEvent();
94
        }
95
96 11
        return $this->event;
97
    }
98
99
    /**
100
     * @param Event $event
101
     */
102 8
    public function setEvent(Event $event)
103
    {
104 8
        $this->event = $event;
105 8
    }
106
107
    /**
108
     * @throws \Exception
109
     */
110 11
    private function loadEvent()
111
    {
112 11
        $request = $this->getRequest();
113 11
        $eventKey = 'event';
114
115 11
        if (!isset($request[$eventKey])) {
116 3
            return;
117
        }
118
119 8
        $request = $request[$eventKey];
120 8
        if (!isset($request['type'])) {
121 1
            throw new BotonomousException(self::MISSING_EVENT_TYPE_MESSAGE);
122
        }
123
124
        // create the event
125 7
        $eventObject = new Event($request['type']);
126
127
        // exclude type from the args since it's already passed
128 7
        unset($request['type']);
129
130 7
        $stringUtility = new StringUtility();
131 7
        foreach ($request as $argKey => $argValue) {
132 7
            if (array_key_exists($argKey, $this->requestEventMaps)) {
133 6
                $argKey = $this->requestEventMaps[$argKey];
134
            }
135
136 7
            $setterName = 'set'.$stringUtility->snakeCaseToCamelCase($argKey);
137
138
            // ignore calling the method if setter does not exist
139 7
            if (!method_exists($eventObject, $setterName)) {
140 6
                continue;
141
            }
142
143 7
            $eventObject->$setterName($argValue);
144
        }
145
146
        // set it
147 7
        $this->setEvent($eventObject);
148 7
    }
149
150
    /**
151
     * @throws \Exception
152
     *
153
     * @return array<string,boolean|string>
0 ignored issues
show
Documentation introduced by
Should the return type not be array<*,boolean|string>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
154
     */
155 3
    public function verifyOrigin()
156
    {
157 3
        $request = $this->getRequest();
158
159 3
        if (!isset($request['token']) || !isset($request['api_app_id'])) {
160
            return [
161 1
                parent::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
162 1
                parent::ORIGIN_VERIFICATION_MESSAGE_KEY => self::MISSING_TOKEN_OR_APP_ID_MESSAGE,
163
            ];
164
        }
165
166 3
        $verificationToken = $this->getConfig()->get('verificationToken');
167
168 3
        if (empty($verificationToken)) {
169 1
            throw new BotonomousException('Verification token must be provided');
170
        }
171
172 2
        $expectedAppId = $this->getConfig()->get('appId');
173
174 2
        if (empty($expectedAppId)) {
175 1
            throw new BotonomousException(self::MISSING_APP_ID_MESSAGE);
176
        }
177
178 1
        if ($verificationToken === $request['token'] &&
179 1
            $expectedAppId === $request['api_app_id']) {
180
            return [
181 1
                parent::ORIGIN_VERIFICATION_SUCCESS_KEY => true,
182 1
                parent::ORIGIN_VERIFICATION_MESSAGE_KEY => 'O La la!',
183
            ];
184
        }
185
186
        return [
187 1
            parent::ORIGIN_VERIFICATION_SUCCESS_KEY => false,
188 1
            parent::ORIGIN_VERIFICATION_MESSAGE_KEY => 'Token or api_app_id mismatch',
189
        ];
190
    }
191
192
    /**
193
     * Check if the request belongs to the bot itself.
194
     *
195
     * @throws \Exception
196
     *
197
     * @return bool
198
     */
199 4
    public function isThisBot(): bool
200
    {
201 4
        $subType = $this->getRequest('subtype');
202
203 4
        if ($subType === 'bot_message') {
204 1
            return true;
205
        }
206
207 4
        $event = $this->getEvent();
208
209 4
        return $event instanceof Event && !empty($event->getBotId());
210
    }
211
212
    /**
213
     * @throws \Exception
214
     *
215
     * @return string
216
     */
217 1
    public function getChannelId(): string
218
    {
219 1
        return $this->getEvent()->getChannel();
220
    }
221
}
222