class Client in Acquia Connector 8
Same name and namespace in other branches
- 8.2 src/Client.php \Drupal\acquia_connector\Client
- 3.x src/Client.php \Drupal\acquia_connector\Client
Acquia connector client.
@package Drupal\acquia_connector
Hierarchy
- class \Drupal\acquia_connector\Client uses LoggerChannelTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Client
5 files declare their use of Client
- AcquiaConnectorUnitTest.php in tests/
src/ Unit/ AcquiaConnectorUnitTest.php - Contains \Drupal\Tests\acquia_connector\Unit\AcquiaConnectorUnitTest.
- CredentialForm.php in src/
Form/ CredentialForm.php - SettingsForm.php in src/
Form/ SettingsForm.php - SetupForm.php in src/
Form/ SetupForm.php - SpiController.php in src/
Controller/ SpiController.php
1 string reference to 'Client'
1 service uses Client
File
- src/
Client.php, line 20
Namespace
Drupal\acquia_connectorView source
class Client {
use LoggerChannelTrait;
use StringTranslationTrait;
use MessengerTrait;
/**
* The HTTP client to fetch the feed data with.
*
* @var \GuzzleHttp\Client
*/
protected $client;
/**
* Request headers.
*
* @var array
*/
protected $headers;
/**
* Acquia SPI server.
*
* @var string
*/
protected $server;
/**
* Config Factory Interface.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $config;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Client constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* Config Factory Interface.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
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,
]);
}
/**
* Get account settings to use for creating request authorizations.
*
* @param string $email
* Acquia account email.
* @param string $password
* Plain-text password for Acquia account. Will be hashed for communication.
*
* @return array|false
* Credentials array or FALSE.
*
* @throws \Drupal\acquia_connector\ConnectorException
*/
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,
];
// Don't use nspiCall() - key is not defined yet.
$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,
];
// Don't use nspiCall() - key is not defined yet.
$response = $this
->request('POST', '/agent-api/subscription/credentials', $data);
if ($response['body']) {
return $response['body'];
}
}
return FALSE;
}
/**
* Get Acquia subscription from Acquia.
*
* @param string $id
* Acquia Subscription ID.
* @param string $key
* Acquia Subscription key.
* @param array $body
* Optional.
*
* @return array|false
* Acquia Subscription array or FALSE.
*
* @throws \Exception
*/
public function getSubscription($id, $key, array $body = []) {
$body['identifier'] = $id;
// There is an identifier and key, so attempt communication.
$subscription = [];
$this->state
->set('acquia_subscription_data.timestamp', \Drupal::time()
->getRequestTime());
// Include version number information.
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'];
// Subscription activated.
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;
}
/**
* Get information on Acquia Search modules.
*
* @return array|null
* Versions for enabled search modules, NULL otherwise.
*/
protected function getSearchModulesData() : ?array {
// This is the only search module compatible with this version of Acquia
// Connector for now.
if (!\Drupal::moduleHandler()
->moduleExists('acquia_search')) {
return NULL;
}
// Include Acquia Search Solr for Search API module version number.
$modules = [
'acquia_search',
'search_api_solr',
];
$result = [];
foreach ($modules as $name) {
$extension_list = \Drupal::service('extension.list.module');
$info = $extension_list
->getExtensionInfo($name);
// Send the version, or at least the core compatibility as a fallback.
$result[$name] = isset($info['version']) ? (string) $info['version'] : (string) $info['core_version_requirement'];
}
return $result;
}
/**
* Get Acquia subscription from Acquia.
*
* @param string $id
* Acquia Subscription ID.
* @param string $key
* Acquia Subscription key.
* @param array $body
* Optional.
*
* @return array|false
* Response result or FALSE.
*/
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;
}
/**
* Get SPI definition.
*
* @param string $apiEndpoint
* API endpoint.
*
* @return array|bool
* Definition array or FALSE.
*/
public function getDefinition($apiEndpoint) {
try {
return $this
->request('GET', $apiEndpoint, []);
} catch (ConnectorException $e) {
$this
->getLogger('acquia connector')
->error($e
->getCustomMessage());
}
return FALSE;
}
/**
* Validate the response authenticator.
*
* @param string $key
* Acquia Subscription key.
* @param array $response
* Response.
* @param array $requestAuthenticator
* Authenticator array.
*
* @return bool
* TRUE if valid response, FALSE otherwise.
*/
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'];
}
/**
* Create and send a request.
*
* @param string $method
* Method to call.
* @param string $path
* Path to call.
* @param array $data
* Data to send.
*
* @return array|false
* Response array or FALSE.
*
* @throws ConnectorException
*/
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;
}
/**
* Build authenticator to sign requests to the Acquia.
*
* @param string $key
* Secret key to use for signing the request.
* @param int $request_time
* Such as from \Drupal::time()->getRequestTime().
* @param array $params
* Optional parameters to include.
* 'identifier' - Network Identifier.
*
* @return array
* Authenticator array.
*/
protected function buildAuthenticator($key, int $request_time, array $params = []) {
$authenticator = [];
if (isset($params['identifier'])) {
// Put Subscription ID in authenticator but do not use in hash.
$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;
}
/**
* Calculates a HMAC-SHA1 according to RFC2104.
*
* @param string $key
* Key.
* @param int $time
* Timestamp.
* @param string $nonce
* Nonce.
*
* @return string
* HMAC-SHA1 hash.
*
* @see http://www.ietf.org/rfc/rfc2104.txt
*/
protected function hash($key, $time, $nonce) {
$string = $time . ':' . $nonce;
return CryptConnector::acquiaHash($key, $string);
}
/**
* Get a random base 64 encoded string.
*
* @return string
* Random base 64 encoded string.
*/
protected function getNonce() {
return Crypt::hashBase64(uniqid(mt_rand(), TRUE) . random_bytes(55));
}
/**
* Prepare and send a REST request to Acquia with an authenticator.
*
* @param string $method
* HTTP method.
* @param array $params
* Parameters to pass to the NSPI.
* @param string $key
* Acquia Key or NULL.
*
* @return array
* NSPI response.
*
* @throws ConnectorException
*/
public function nspiCall($method, array $params, $key = NULL) {
if (empty($key)) {
$storage = new Storage();
$key = $storage
->getKey();
}
// Used in HMAC validation.
$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;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Client:: |
protected | property | The HTTP client to fetch the feed data with. | |
Client:: |
protected | property | Config Factory Interface. | |
Client:: |
protected | property | The config factory. | |
Client:: |
protected | property | Request headers. | |
Client:: |
protected | property | Acquia SPI server. | |
Client:: |
protected | property | The state service. | |
Client:: |
protected | function | Build authenticator to sign requests to the Acquia. | 1 |
Client:: |
public | function | Get SPI definition. | |
Client:: |
protected | function | Get a random base 64 encoded string. | |
Client:: |
protected | function | Get information on Acquia Search modules. | |
Client:: |
public | function | Get Acquia subscription from Acquia. | |
Client:: |
public | function | Get account settings to use for creating request authorizations. | |
Client:: |
protected | function | Calculates a HMAC-SHA1 according to RFC2104. | |
Client:: |
public | function | Prepare and send a REST request to Acquia with an authenticator. | |
Client:: |
protected | function | Create and send a request. | |
Client:: |
public | function | Get Acquia subscription from Acquia. | |
Client:: |
protected | function | Validate the response authenticator. | |
Client:: |
public | function | Client constructor. | 1 |
LoggerChannelTrait:: |
protected | property | The logger channel factory service. | |
LoggerChannelTrait:: |
protected | function | Gets the logger for a specific channel. | |
LoggerChannelTrait:: |
public | function | Injects the logger channel factory. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |