View source
<?php
namespace Drupal\acquia_connector;
use Drupal\acquia_connector\Helper\Storage;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use GuzzleHttp\Exception\RequestException;
class Client {
use LoggerChannelTrait;
use StringTranslationTrait;
use MessengerTrait;
protected $client;
protected $headers;
protected $server;
protected $config;
protected $configFactory;
protected $state;
public function __construct(ConfigFactoryInterface $configFactory, StateInterface $state) {
$this->configFactory = $configFactory;
$this->config = $configFactory
->get('acquia_connector.settings');
$this->server = $this->config
->get('spi.server');
$this->state = $state;
$this->headers = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
];
$this->client = \Drupal::service('http_client_factory')
->fromOptions([
'verify' => (bool) $this->config
->get('spi.ssl_verify'),
'http_errors' => FALSE,
]);
}
public function getSubscriptionCredentials($email, $password) {
$body = [
'email' => $email,
];
$authenticator = $this
->buildAuthenticator($email, \Drupal::time()
->getRequestTime(), [
'rpc_version' => ACQUIA_CONNECTOR_ACQUIA_SPI_DATA_VERSION,
]);
$data = [
'body' => $body,
'authenticator' => $authenticator,
];
$communication_setting = $this
->request('POST', '/agent-api/subscription/communication', $data);
if ($communication_setting) {
$crypt_pass = new CryptConnector($communication_setting['algorithm'], $password, $communication_setting['hash_setting'], $communication_setting['extra_md5']);
$pass = $crypt_pass
->cryptPass();
$body = [
'email' => $email,
'pass' => $pass,
'rpc_version' => ACQUIA_CONNECTOR_ACQUIA_SPI_DATA_VERSION,
];
$authenticator = $this
->buildAuthenticator($pass, \Drupal::time()
->getRequestTime(), [
'rpc_version' => ACQUIA_CONNECTOR_ACQUIA_SPI_DATA_VERSION,
]);
$data = [
'body' => $body,
'authenticator' => $authenticator,
];
$response = $this
->request('POST', '/agent-api/subscription/credentials', $data);
if ($response['body']) {
return $response['body'];
}
}
return FALSE;
}
public function getSubscription($id, $key, array $body = []) {
$body['identifier'] = $id;
$subscription = [];
$this->state
->set('acquia_subscription_data.timestamp', \Drupal::time()
->getRequestTime());
acquia_connector_load_versions();
if (IS_ACQUIA_DRUPAL) {
$body['version'] = ACQUIA_DRUPAL_VERSION;
$body['series'] = ACQUIA_DRUPAL_SERIES;
$body['branch'] = ACQUIA_DRUPAL_BRANCH;
$body['revision'] = ACQUIA_DRUPAL_REVISION;
}
if ($search_info = $this
->getSearchModulesData()) {
$body['search_version'] = $search_info;
}
try {
$response = $this
->nspiCall('/agent-api/subscription', $body);
if (!empty($response['result']['authenticator']) && $this
->validateResponse($key, $response['result'], $response['authenticator'])) {
$subscription += $response['result']['body'];
if (is_numeric($this->state
->get('acquia_subscription_data')) && is_array($response['result']['body'])) {
$this->state
->set('acquia_subscription_data', $subscription);
}
return $subscription;
}
} catch (ConnectorException $e) {
$this
->messenger()
->addError($this
->t('Error occurred while retrieving Acquia subscription information. See logs for details.'));
if ($e
->isCustomized()) {
$this
->getLogger('acquia connector')
->error($e
->getCustomMessage() . '. Response data: @data', [
'@data' => json_encode($e
->getAllCustomMessages()),
]);
}
else {
$this
->getLogger('acquia connector')
->error($e
->getMessage());
}
throw $e;
}
return FALSE;
}
protected function getSearchModulesData() : ?array {
if (!\Drupal::moduleHandler()
->moduleExists('acquia_search_solr')) {
return NULL;
}
$modules = [
'acquia_search_solr',
'search_api_solr',
];
$result = [];
foreach ($modules as $name) {
$extension_list = \Drupal::service('extension.list.module');
$info = $extension_list
->getExtensionInfo($name);
$result[$name] = isset($info['version']) ? (string) $info['version'] : (string) $info['core_version_requirement'];
}
return $result;
}
public function sendNspi($id, $key, array $body = []) {
$body['identifier'] = $id;
try {
$response = $this
->nspiCall('/spi-api/site', $body);
if (!empty($response['result']['authenticator']) && $this
->validateResponse($key, $response['result'], $response['authenticator'])) {
return $response['result'];
}
} catch (ConnectorException $e) {
$this
->getLogger('acquia connector')
->error('Error: ' . $e
->getCustomMessage());
}
return FALSE;
}
public function getDefinition($apiEndpoint) {
try {
return $this
->request('GET', $apiEndpoint, []);
} catch (ConnectorException $e) {
$this
->getLogger('acquia connector')
->error($e
->getCustomMessage());
}
return FALSE;
}
protected function validateResponse($key, array $response, array $requestAuthenticator) {
$responseAuthenticator = $response['authenticator'];
if (!($requestAuthenticator['nonce'] === $responseAuthenticator['nonce'] && $requestAuthenticator['time'] < $responseAuthenticator['time'])) {
return FALSE;
}
$hash = $this
->hash($key, $responseAuthenticator['time'], $responseAuthenticator['nonce']);
return $hash === $responseAuthenticator['hash'];
}
protected function request($method, $path, array $data) {
$uri = $this->server . $path;
$options = [
'headers' => $this->headers,
'body' => Json::encode($data),
];
try {
switch ($method) {
case 'GET':
$response = $this->client
->get($uri);
$status_code = $response
->getStatusCode();
$stream_size = $response
->getBody()
->getSize();
$data = Json::decode($response
->getBody()
->read($stream_size));
if ($status_code < 200 || $status_code > 299) {
throw new ConnectorException($data['message'], $data['code'], $data);
}
return $data;
case 'POST':
$response = $this->client
->post($uri, $options);
$status_code = $response
->getStatusCode();
$stream_size = $response
->getBody()
->getSize();
$data = Json::decode($response
->getBody()
->read($stream_size));
if ($status_code < 200 || $status_code > 299) {
throw new ConnectorException($data['message'], $data['code'], $data);
}
return $data;
}
} catch (RequestException $e) {
throw new ConnectorException($e
->getMessage(), $e
->getCode());
}
return FALSE;
}
protected function buildAuthenticator($key, int $request_time, array $params = []) {
$authenticator = [];
if (isset($params['identifier'])) {
$authenticator['identifier'] = $params['identifier'];
unset($params['identifier']);
}
$nonce = $this
->getNonce();
$authenticator['time'] = $request_time;
$authenticator['hash'] = $this
->hash($key, $request_time, $nonce);
$authenticator['nonce'] = $nonce;
return $authenticator;
}
protected function hash($key, $time, $nonce) {
$string = $time . ':' . $nonce;
return CryptConnector::acquiaHash($key, $string);
}
protected function getNonce() {
return Crypt::hashBase64(uniqid(mt_rand(), TRUE) . random_bytes(55));
}
public function nspiCall($method, array $params, $key = NULL) {
if (empty($key)) {
$storage = new Storage();
$key = $storage
->getKey();
}
$params['rpc_version'] = ACQUIA_CONNECTOR_ACQUIA_SPI_DATA_VERSION;
$ip = \Drupal::request()->server
->get('SERVER_ADDR', '');
$host = \Drupal::request()->server
->get('HTTP_HOST', '');
$ssl = \Drupal::request()
->isSecure();
$data = [
'authenticator' => $this
->buildAuthenticator($key, \Drupal::time()
->getRequestTime(), $params),
'ip' => $ip,
'host' => $host,
'ssl' => $ssl,
'body' => $params,
];
$data['result'] = $this
->request('POST', $method, $data);
return $data;
}
}