acquia_connector-8.x-1.22/src/Subscription.php

src/Subscription.php
<?php

namespace Drupal\acquia_connector;

use Drupal\acquia_connector\Client\ClientFactory;
use Drupal\acquia_connector\Event\AcquiaSubscriptionDataEvent;
use Drupal\acquia_connector\Event\AcquiaSubscriptionSettingsEvent;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Acquia Subscription service.
 *
 * The Acquia Subscription service is the public way other items can access
 * Acquia's services via connector. There is a settings object that is invoked
 * via an Event Subscriber, to fetch settings from envvars, settings.php or the
 * state system.
 *
 * Acquia Subscription data is always stored in state, and is not part of the
 * settings object.
 *
 * @package Drupal\acquia_connector.
 */
class Subscription {

  use StringTranslationTrait;

  /**
   * Errors defined by Acquia.
   */
  const NOT_FOUND = 1000;
  const KEY_MISMATCH = 1100;
  const EXPIRED = 1200;
  const REPLAY_ATTACK = 1300;
  const KEY_NOT_FOUND = 1400;
  const MESSAGE_FUTURE = 1500;
  const MESSAGE_EXPIRED = 1600;
  const MESSAGE_INVALID = 1700;
  const VALIDATION_ERROR = 1800;
  const PROVISION_ERROR = 9000;

  /**
   * Subscription message lifetime defined by Acquia.
   */
  // 15 * 60.
  const MESSAGE_LIFETIME = 900;

  /**
   * Event Dispatcher Service.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $dispatcher;

  /**
   * Settings Provider.
   *
   * @var string
   */
  protected $settingsProvider;

  /**
   * Settings object.
   *
   * @var \Drupal\acquia_connector\Settings
   */
  protected $settings;

  /**
   * Raw Acquia subscription data.
   *
   * @var array
   */
  protected $subscriptionData;

  /**
   * Connector Client.
   *
   * @var \Drupal\acquia_connector\Client\ClientFactory
   */
  protected $clientFactory;

  /**
   * Drupal config.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Drupal State Service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * Acquia Subscription Constructor.
   *
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
   *   The event dispatcher.
   * @param \Drupal\acquia_connector\Client\ClientFactory $client_factory
   *   The acquia connector client factory.
   * @param \Drupal\Core\State\StateInterface $state
   *   Drupal State System.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config Factory.
   */
  public function __construct(EventDispatcherInterface $dispatcher, ClientFactory $client_factory, StateInterface $state, ConfigFactoryInterface $config_factory) {
    $this->dispatcher = $dispatcher;
    $this->state = $state;
    $this->clientFactory = $client_factory;
    $this->configFactory = $config_factory;
  }

  /**
   * Call the event to populate Acquia Connector settings.
   */
  public function populateSettings() {
    $event = new AcquiaSubscriptionSettingsEvent($this->configFactory);

    // @todo Remove after dropping support for Drupal 8.
    if (version_compare(\Drupal::VERSION, '9.1', '>=')) {
      $this->dispatcher->dispatch($event, AcquiaConnectorEvents::GET_SETTINGS);
    }
    else {
      // @phpstan-ignore-next-line
      $this->dispatcher->dispatch(AcquiaConnectorEvents::GET_SETTINGS, $event);
    }

    $this->settings = $event->getSettings();
    $this->settingsProvider = $event->getProvider();
  }

  /**
   * Retrieves the stored subscription.
   *
   * @return \Drupal\acquia_connector\Settings
   *   The Connector Settings Object.
   */
  public function getSettings() {
    if (!$this->settings || !$this->settings->getIdentifier()) {
      $this->populateSettings();
    }
    return $this->settings;
  }

  /**
   * Gets the subscription provider from the subscription event for settings.
   *
   * @return string
   *   The name of settings' provider.
   */
  public function getProvider() {
    if (!$this->settings || !$this->settings->getIdentifier()) {
      $this->populateSettings();
    }
    return $this->settingsProvider;
  }

  /**
   * Retrieve the Acquia Subscription.
   *
   * @return array
   *   The Raw Subscription Data.
   */
  public function getSubscription($refresh = NULL, $body = []) {
    // If Settings do not exist, we have no subscription to fetch.
    if (!$this->hasCredentials()) {
      // Ensure subscription data is scrubbed.
      $this->state->delete('acquia_connector.subscription_data');
      Cache::invalidateTags(['acquia_connector_subscription']);
      return ['active' => FALSE];
    }
    // Used the cached data if refresh is NULL or FALSE.
    if (isset($this->subscriptionData) && $refresh !== TRUE) {
      return $this->subscriptionData;
    }
    $subscriptionData = $this->state->get('acquia_connector.subscription_data', []);
    // Handle edge cases where acquia_connector.subscription_data is set wrong.
    if (!is_array($subscriptionData)) {
      $subscriptionData = [];
    }
    if ($subscriptionData !== [] && $refresh !== TRUE) {
      // Ensure the legacy location of UUID is up-to-date.
      $subscriptionData['uuid'] = $this->getSettings()->getApplicationUuid();
      return $subscriptionData;
    }
    // If there is no local subscription data, retrieve it.
    $subscriptionData += $this->getDefaultSubscriptionData();
    try {
      $client = $this->clientFactory->getCloudApiClient();
      $application_response = $client->get('/api/applications/' . $this->getSettings()->getApplicationUuid());
      $application_data = Json::decode((string) $application_response->getBody());
      $subscription_uuid = $application_data['subscription']['uuid'];
      $subscription_response = $client->get('/api/subscriptions/' . $subscription_uuid);
      $subscription_info = Json::decode((string) $subscription_response->getBody());

      $subscriptionData['active'] = $subscription_info['flags']['active'];
      $subscriptionData['application'] = $application_data;
      $subscriptionData['subscription_name'] = $subscription_info['name'];
      // Expiration Date may have been a string, ensure its set as an array.
      $subscriptionData['expiration_date'] = [
        'value' => $subscription_info['expire_at'],
      ];
    }
    catch (RequestException | ConnectorException $e) {
    }

    // If subscription expiration date passed, set gratis value to TRUE.
    if (isset($subscriptionData['expiration_date']['value'])) {
      $subscriptionData['gratis'] = strtotime($subscriptionData['expiration_date']['value']) < strtotime(date("m/d/Y"));
    }
    // Allow other modules to add metadata to the subscription.
    $event = new AcquiaSubscriptionDataEvent($this->configFactory, $subscriptionData);
    // @todo Remove after dropping support for Drupal 8.
    if (version_compare(\Drupal::VERSION, '9.1', '>=')) {
      $this->dispatcher->dispatch($event, AcquiaConnectorEvents::GET_SUBSCRIPTION);
    }
    else {
      // @phpstan-ignore-next-line
      $this->dispatcher->dispatch(AcquiaConnectorEvents::GET_SUBSCRIPTION, $event);
    }
    // Get data from subscription event.
    $this->subscriptionData = $event->getData();

    // Get product data from subscription event.
    $this->subscriptionData['product'] = $event->getProductData();

    // Save subscription data to state.
    $this->state->set('acquia_connector.subscription_data', $this->subscriptionData);
    Cache::invalidateTags(['acquia_connector_subscription']);

    return $this->subscriptionData;
  }

  /**
   * Build a subscription data object to mimic legacy NSPI responses.
   *
   * @return array
   *   The subscription data.
   */
  private function getDefaultSubscriptionData() {
    if (!$this->hasCredentials()) {
      return ['active' => FALSE];
    }

    return [
      'active' => TRUE,
      'gratis' => FALSE,
      'href' => "",
      'uuid' => $this->getSettings()->getApplicationUuid(),
      'subscription_name' => "",
      "expiration_date" => "",
      "search_service_enabled" => 1,
    ];
  }

  /**
   * Delete any subscription data held in the database.
   */
  public function delete() {
    $this->state->set('acquia_connector.subscription_data', ['active' => FALSE]);
    $this->state->delete('spi.site_name');
    $this->state->delete('spi.site_machine_name');
  }

  /**
   * Helper function to check if an identifier and key exist.
   */
  public function hasCredentials() {
    $settings = $this->getSettings();
    return $settings->getIdentifier() && $settings->getSecretKey() && $settings->getApplicationUuid();
  }

  /**
   * Helper function to check if the site has an active subscription.
   */
  public function isActive() {
    $active = FALSE;
    // Subscription cannot be active if we have no credentials.
    if ($this->hasCredentials()) {
      $data = $this->state->get('acquia_connector.subscription_data');
      if ($data !== NULL) {
        if (is_array($data)) {
          return !empty($data['active']);
        }
      }
      // Only retrieve cached subscription at this time.
      $subscription = $this->getSubscription(FALSE);

      // If we don't have a timestamp, or timestamp is less than a day, fetch.
      if (!isset($subscription['timestamp']) || (isset($subscription['timestamp']) && (time() - $subscription['timestamp'] > 60 * 60 * 24))) {
        try {
          $subscription = $this->getSubscription(TRUE, ['no_heartbeat' => 1]);
          $this->state->set('acquia_connector.subscription_data', $subscription);
        }
        catch (ConnectorException $e) {
        }
      }
      $active = !empty($subscription['active']);
    }
    return $active;
  }

  /**
   * Return an error message by the error code.
   *
   * Returns an error message for the most recent (failed) attempt to connect
   * to the Acquia during the current page request. If there were no failed
   * attempts, returns FALSE.
   *
   * This function assumes that the most recent error came from the Acquia;
   * otherwise, it will not work correctly.
   *
   * @param int $errno
   *   Error code defined by the module.
   *
   * @return mixed
   *   The error message string or FALSE.
   */
  public function connectionErrorMessage($errno) {
    if ($errno) {
      switch ($errno) {
        case self::NOT_FOUND:
          return $this->t('The identifier you have provided does not exist at Acquia or is expired. Please make sure you have used the correct value and try again.');

        case self::EXPIRED:
          return $this->t('Your Acquia Subscription subscription has expired. Please renew your subscription so that you can resume using Acquia services.');

        case self::MESSAGE_FUTURE:
          return $this->t('Your server is unable to communicate with Acquia due to a problem with your clock settings. For security reasons, we reject messages that are more than @time ahead of the actual time recorded by our servers. Please fix the clock on your server and try again.', ['@time' => \Drupal::service('date.formatter')->formatInterval(Subscription::MESSAGE_LIFETIME)]);

        case self::MESSAGE_EXPIRED:
          return $this->t('Your server is unable to communicate with Acquia due to a problem with your clock settings. For security reasons, we reject messages that are more than @time older than the actual time recorded by our servers. Please fix the clock on your server and try again.', ['@time' => \Drupal::service('date.formatter')->formatInterval(Subscription::MESSAGE_LIFETIME)]);

        case self::VALIDATION_ERROR:
          return $this->t('The identifier and key you have provided for the Acquia Subscription do not match. Please make sure you have used the correct values and try again.');

        default:
          return $this->t('There is an error communicating with the Acquia Subscription at this time. Please check your identifier and key and try again.');
      }
    }
    return FALSE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc