<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Signifyd\Model\SignifydGateway\Response; use Magento\Framework\Json\DecoderInterface; use Magento\Signifyd\Model\Config; /** * Validates webhook request. * */ class WebhookRequestValidator { /** * Allowed topic identifiers which will be sent in the X-SIGNIFYD-TOPIC header of the webhook. * * @var array */ private $allowedTopicValues = [ 'cases/creation', 'cases/rescore', 'cases/review', 'guarantees/completion', 'cases/test' ]; /** * @var Config */ private $config; /** * @var DecoderInterface */ private $decoder; /** * @param Config $config * @param DecoderInterface $decoder */ public function __construct( Config $config, DecoderInterface $decoder ) { $this->config = $config; $this->decoder = $decoder; } /** * Validates webhook request. * * @param WebhookRequest $webhookRequest * @return bool */ public function validate(WebhookRequest $webhookRequest) { $body = $webhookRequest->getBody(); $eventTopic = $webhookRequest->getEventTopic(); $hash = $webhookRequest->getHash(); return $this->isValidTopic($eventTopic) && $this->isValidBody($body) && $this->isValidHash($eventTopic, $body, $hash); } /** * Checks if value of topic identifier is in allowed list * * @param string $topic topic identifier. * @return bool */ private function isValidTopic($topic) { return in_array($topic, $this->allowedTopicValues); } /** * Verifies a webhook request body is valid JSON and not empty. * * @param string $body * @return bool */ private function isValidBody($body) { try { $decodedBody = $this->decoder->decode($body); } catch (\Exception $e) { return false; } return !empty($decodedBody); } /** * Verifies a webhook request has in fact come from SIGNIFYD. * * @param string $eventTopic * @param string $body * @param string $hash * @return bool */ private function isValidHash($eventTopic, $body, $hash) { // In the case that this is a webhook test, the encoding ABCDE is allowed $apiKey = $eventTopic == 'cases/test' ? 'ABCDE' : $this->config->getApiKey(); $actualHash = base64_encode(hash_hmac('sha256', $body, $apiKey, true)); return $hash === $actualHash; } }