unlock-8.x-1.0/src/Client.php

src/Client.php
<?php

namespace Drupal\unlock;

use Drupal\Component\Utility\Html;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Password\PasswordGeneratorInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\unlock\Form\SettingsForm;
use Drupal\user\UserDataInterface;
use GuzzleHttp\ClientInterface;

/**
 * Client service for interact with Unlock Protocol.
 */
class Client {

  use StringTranslationTrait;

  /**
   * Login base url.
   *
   * @var string
   */
  public const BASE_URL = 'https://app.unlock-protocol.com/checkout';

  /**
   * Validation url.
   *
   * @var string
   */
  public const VALIDATE_URL = 'https://locksmith.unlock-protocol.com/api/oauth';

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The user data service.
   *
   * @var \Drupal\user\UserDataInterface
   */
  protected $userData;

  /**
   * The Guzzle client.
   *
   * @var \GuzzleHttp\ClientInterface
   */
  protected $client;

  /**
   * The password generator service.
   *
   * @var \Drupal\Core\Password\PasswordGeneratorInterface
   */
  protected $passwordGenerator;

  /**
   * Configuration for unlock.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * Constructs a Client object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   * @param \Drupal\user\UserDataInterface $user_data
   * @param \GuzzleHttp\ClientInterface $client
   * @param \Drupal\Core\Password\PasswordGeneratorInterface $password_generator
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, AccountProxyInterface $current_user, UserDataInterface $user_data, ClientInterface $client, PasswordGeneratorInterface $password_generator, ConfigFactoryInterface $config_factory) {
    $this->entityTypeManager = $entity_type_manager;
    $this->currentUser = $current_user;
    $this->userData = $user_data;
    $this->client = $client;
    $this->passwordGenerator = $password_generator;
    $this->config = $config_factory->get(SettingsForm::SETTINGS);
  }

  /**
   * Method description.
   */
  public function getLoginUrl(): string {
    if (empty($_SESSION['unlock_token'])) {
      $state = $this->passwordGenerator->generate(8);
      $_SESSION['unlock_token'] = $state;
    }
    $args = [
      'client_id' => self::getClientId(),
      'redirect_uri' => self::getRedirectUrl(),
      'state' => $_SESSION['unlock_token'],
    ];
    return self::BASE_URL . '?' . http_build_query($args);
  }

  /**
   * @throws \JsonException
   */
  public function getCheckoutUrl($lock_address, $lock_network): string {
    $paywall_locks[$lock_address] = [
      'network' => (int) $lock_network,
    ];
    $paywall_config = [
      'metadataInputs' => [[
        'name' => 'Email',
        'type' => 'email',
        'required' => TRUE,
      ],
      ],
      'locks' => $paywall_locks,
      'pessimistic' => TRUE,
    ];
    $args = [
      'redirectUri' => self::getRedirectUrl(),
      'paywallConfig' => json_encode($paywall_config, JSON_THROW_ON_ERROR),
    ];
    return self::BASE_URL . '?' . urldecode(http_build_query($args));
  }

  /**
   *
   */
  public static function getRedirectUrl(): string {
    $host = \Drupal::request()->getSchemeAndHttpHost();
    $path = \Drupal::request()->getPathInfo();
    return $host . $path;
  }

  /**
   *
   */
  public static function getClientId(): string {
    return \Drupal::request()->getHost();
  }

  /**
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \GuzzleHttp\Exception\GuzzleException
   * @throws \JsonException
   */
  public function authenticate(): void {
    if (!$this->needAuthenticate()) {
      return;
    }
    $code = \Drupal::request()->query->get('code');
    $unlock_token = \Drupal::request()->query->get('state');
    if (!$code || empty($_SESSION['unlock_token']) || ($_SESSION['unlock_token'] !== $unlock_token)) {
      return;
    }
    $ethereum_address = $this->validateAuthCode($code);
    $ethereum_address = Html::escape($ethereum_address);

    if ($this->currentUser->isAnonymous()) {
      $users = $this->userData->get('unlock', NULL, 'ethereum_address');
      if ($users) {
        $uid = array_key_first($users);
        /** @var \Drupal\user\UserInterface $user */
        $user = $this->entityTypeManager
          ->getStorage('user')
          ->load($uid);
        if ($user) {
          user_login_finalize($user);
          return;
        }
      }
    }

    if ($this->currentUser->isAuthenticated()) {
      $this->userData->set('unlock', $this->currentUser->id(), 'ethereum_address', $ethereum_address);
      return;
    }

    $email = self::getEmail($ethereum_address);
    $users = $this->entityTypeManager->getStorage('user')->loadByProperties(['mail' => $email]);
    if ($users) {
      /** @var \Drupal\user\UserInterface $user */
      $user = reset($users);
      $this->userData->set('unlock', $user->id(), 'ethereum_address', $ethereum_address);
      user_login_finalize($user);
      return;
    }

    // @todo Check if global settings allow automatic registration
    /** @var \Drupal\user\UserInterface $user */
    $user = $this->entityTypeManager
      ->getStorage('user')
      ->create();
    // Mandatory settings.
    $user->setPassword($this->passwordGenerator->generate());
    $user->enforceIsNew();
    $user->setEmail($email);
    $user->setUsername($ethereum_address);
    $user->activate();
    $user->save();
    $this->userData->set('unlock', $user->id(), 'ethereum_address', $ethereum_address);
    user_login_finalize($user);
  }

  /**
   * Check that user logged in and have etherium address associated.
   *
   * @return bool
   */
  public function needAuthenticate(): bool {
    $result = TRUE;
    if ($this->currentUser->isAuthenticated()) {
      $ethereum_address = $this->userData->get('unlock', $this->currentUser->id(), 'ethereum_address');
      if ($ethereum_address) {
        $result = FALSE;
      }
    }
    return $result;
  }

  /**
   * @param $code
   *
   * @return mixed
   * @throws \GuzzleHttp\Exception\GuzzleException
   * @throws \JsonException
   */
  public function validateAuthCode($code) {
    $response = $this->client->request('POST', self::VALIDATE_URL, [
      'json' => [
        'grant_type' => 'authorization_code',
        'client_id' => self::getClientId(),
        'redirect_uri' => self::getRedirectUrl(),
        'code' => $code,
      ],
    ]);
    $json = (string) $response->getBody();
    $json = json_decode($json, TRUE, 512, JSON_THROW_ON_ERROR);
    if (empty($json) || !array_key_exists('me', $json)) {
      throw new \Exception('Invalid account');
    }
    return $json['me'];
  }

  /**
   *
   */
  public static function getEmail($ethereum_address): string {
    return sprintf('%1$s@%2$s', Html::escape($ethereum_address), Html::escape(self::getClientId()));
  }

  /**
   * @param $rpc_endpoint
   * @param $lock_address
   * @param null $user_ethereum_address
   *
   * @return bool
   * @throws \GuzzleHttp\Exception\GuzzleException
   * @throws \JsonException
   */
  public function validate($rpc_endpoint, $lock_address, $user_ethereum_address = NULL): bool {
    if (!$user_ethereum_address) {
      $user_ethereum_address = $this->userData->get('unlock', $this->currentUser->id(), 'ethereum_address');
    }
    $user_ethereum_address = substr($user_ethereum_address, 2);

    $response = $this->client->request('POST', $rpc_endpoint, [
      'json' => [
        'method' => 'eth_call',
        'params' => [
          [
            'to' => $lock_address,
            'data' => sprintf('0x6d8ea5b4000000000000000000000000%s', $user_ethereum_address),
          ],
          'latest',
        ],
        'id' => 31337,
        'jsonrpc' => '2.0',
      ],
    ]);
    $json = (string) $response->getBody();
    $json = json_decode($json, TRUE, 512, JSON_THROW_ON_ERROR);
    return !empty($json['result']) && (hexdec($json['result']) == 1);
  }

  /**
   * @param $entity
   *
   * @return array|false
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \GuzzleHttp\Exception\GuzzleException
   * @throws \JsonException
   */
  public function getUnlockButton($entity) {
    if (!$entity) {
      return FALSE;
    }
    if (!($entity instanceof ThirdPartySettingsInterface)) {
      return FALSE;
    }
    if (!$entity->getThirdPartySetting('unlock', 'status')) {
      return FALSE;
    }
    $result = FALSE;

    $visible_until_paid = $entity->getThirdPartySetting('unlock', 'type');
    $type_word = $visible_until_paid ? 'hide' : 'show';
    $this->authenticate();
    $need_login = $this->needAuthenticate();

    if ($need_login) {
      $button_text = $entity->getThirdPartySetting('unlock', 'login_text') ?: SettingsForm::LOGIN_TEXT;
      $matches = NULL;
      if (preg_match('~<link>(.*?)</link>~iu', $button_text, $matches)) {
        $button_text = str_replace([$matches[0], '@type'], ['{{ link }}', $type_word], $button_text);
        $result = [
          '#type' => 'inline_template',
          '#template' => $this->t($button_text)->__toString(),
          '#context' => [
            'link' => [
              '#type' => 'link',
              '#title' => $this->t($matches[1], ['@type' => $type_word]),
              '#url' => Url::fromUri($this->getLoginUrl()),
              '#cache' => [
                'max-age' => 0,
              ],
            ],
          ],
        ];
      }
      else {
        $result = [
          '#type' => 'link',
          '#title' => $this->t($button_text, ['@type' => $type_word]),
          '#url' => Url::fromUri($this->getLoginUrl()),
          '#cache' => [
            'max-age' => 0,
          ],
        ];
      }
    }
    else {
      $lock_address = $entity->getThirdPartySetting('unlock', 'lock_address');
      $lock_network = $entity->getThirdPartySetting('unlock', 'network');
      if (!$lock_network) {
        $lock_network = $this->config->get('network') ?: 1;
      }
      $url = SettingsForm::RPC_ENDPOINTS[$lock_network];
      $has_unlocked = $this->validate($url, $lock_address);
      if (!$has_unlocked) {
        $button_text = $entity->getThirdPartySetting('unlock', 'checkout_text') ?: SettingsForm::CHECKOUT_TEXT;
        $matches = NULL;
        if (preg_match('~<link>(.*?)</link>~iu', $button_text, $matches)) {
          $button_text = str_replace([$matches[0], '@type'], ['{{ link }}', $type_word], $button_text);
          $result = [
            '#type' => 'inline_template',
            '#template' => $this->t($button_text)->__toString(),
            '#context' => [
              'link' => [
                '#type' => 'link',
                '#title' => $this->t($matches[1], ['@type' => $type_word]),
                '#url' => Url::fromUri($this->getCheckoutUrl($lock_address, $lock_network)),
                '#cache' => [
                  'max-age' => 0,
                ],
              ],
            ],
          ];
        }
        else {
          $result = [
            '#type' => 'link',
            '#title' => $this->t($button_text, ['@type' => $type_word]),
            '#url' => Url::fromUri($this->getCheckoutUrl($lock_address, $lock_network)),
            '#cache' => [
              'max-age' => 0,
            ],
          ];
        }
      }
    }
    if ($result) {
      $result = [
        '#theme' => 'unlock_button',
        '#label' => $result,
        '#type' => $type_word,
        '#level' => $need_login ? 'login' : 'checkout',
      ];
    }
    return $result;
  }

}

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

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