You are here

public function ContentHubWebhookController::validateWebhookSignature in Acquia Content Hub 8

Same name and namespace in other branches
  1. 8.2 src/Controller/ContentHubWebhookController.php \Drupal\acquia_contenthub\Controller\ContentHubWebhookController::validateWebhookSignature()

Validates a webhook signature.

Parameters

\Symfony\Component\HttpFoundation\Request $request: The Webhook Request.

Return value

bool TRUE if signature verification passes, FALSE otherwise.

1 call to ContentHubWebhookController::validateWebhookSignature()
ContentHubWebhookController::receiveWebhook in src/Controller/ContentHubWebhookController.php
Process a webhook.

File

src/Controller/ContentHubWebhookController.php, line 198

Class

ContentHubWebhookController
Controller for Content Hub Imported Entities.

Namespace

Drupal\acquia_contenthub\Controller

Code

public function validateWebhookSignature(Request $request) {
  $headers = array_map('current', $request->headers
    ->all());
  $webhook = $request
    ->getContent();

  // Quick validation to make sure we are not replaying a request
  // from the past.
  $request_date = isset($headers['date']) ? $headers['date'] : "1970";
  $request_timestamp = strtotime($request_date);
  $timestamp = time();

  // Due to networking delays and mismatched clocks, we are making the request
  // accepting window 60s.
  if (abs($request_timestamp - $timestamp) > 900) {
    $message = new FormattableMarkup('The Webhook request seems that was issued in the past [Request timestamp = @t1, server timestamp = @t2]: rejected: @whook', [
      '@t1' => $request_timestamp,
      '@t2' => $timestamp,
      '@whook' => print_r($webhook, TRUE),
    ]);
    $this->loggerFactory
      ->get('acquia_contenthub')
      ->debug($message);
    return FALSE;
  }
  $authorization_header = isset($headers['authorization']) ? $headers['authorization'] : '';

  // Reading type of webhook request.
  $webhook_array = Json::decode($webhook);
  $status = $webhook_array['status'];
  $authorization = '';

  // Constructing the message to sign.
  switch ($status) {
    case 'shared_secret_regenerated':
      $this->contentHubSubscription
        ->getSettings();
      $secret_key = $this->contentHubSubscription
        ->getSharedSecret();
      $signature = $this->clientManager
        ->getRequestSignature($request, $secret_key);
      $authorization = 'Acquia Webhook:' . $signature;
      $this->loggerFactory
        ->get('acquia_contenthub')
        ->debug('Received Webhook for shared secret regeneration. Settings updated.');
      break;
    case 'successful':
    case 'processing':
    case 'in-queue':
    case 'failed':
      $secret_key = $this->contentHubSubscription
        ->getSharedSecret();
      $signature = $this->clientManager
        ->getRequestSignature($request, $secret_key);
      $authorization = 'Acquia Webhook:' . $signature;
      break;
    case 'pending':
      $api = $this->config
        ->get('api_key');
      $secret_key = $this->config
        ->get('secret_key');
      $signature = $this->clientManager
        ->getRequestSignature($request, $secret_key);
      $authorization = "Acquia {$api}:" . $signature;
      break;
  }

  // Log debug information if validation fails.
  if ($authorization !== $authorization_header) {
    $message = new FormattableMarkup('The Webhook request failed HMAC validation. [authorization = %authorization]. [authorization_header = %authorization_header]', [
      '%authorization' => $authorization,
      '%authorization_header' => $authorization_header,
    ]);
    $this->loggerFactory
      ->get('acquia_contenthub')
      ->debug($message);
  }
  return (bool) ($authorization === $authorization_header);
}