You are here

class SearchApiAcquiaSearchConnection in Acquia Search for Search API 7.2

Same name and namespace in other branches
  1. 7 includes/SearchApiAcquiaSearchConnection.php \SearchApiAcquiaSearchConnection

Starting point for the Solr API. Represents a Solr server resource and has methods for pinging, adding, deleting, committing, optimizing and searching.

Hierarchy

Expanded class hierarchy of SearchApiAcquiaSearchConnection

File

includes/v2/SearchApiAcquiaSearchConnection.php, line 8

View source
class SearchApiAcquiaSearchConnection extends SearchApiSolrConnection {

  /**
   * The derived key used to HMAC hash the search request.
   *
   * @var string
   */
  protected $derivedKey;

  /**
   * Creates an authenticator based on a data string and HMAC-SHA1.
   *
   * @see acquia_search_authenticator()
   */
  function authenticator($string, $nonce, $derived_key = NULL) {
    if (empty($derived_key)) {
      $derived_key = $this
        ->getDerivedKey();
    }
    if (empty($derived_key)) {

      // Expired or invalid subscription - don't continue.
      return '';
    }
    else {
      $time = time();
      $hash = hash_hmac('sha1', $time . $nonce . $string, $derived_key);
      return 'acquia_solr_time=' . $time . '; acquia_solr_nonce=' . $nonce . '; acquia_solr_hmac=' . $hash . ';';
    }
  }

  /**
   * Sets the derived key used to HMAC hash the search request.
   *
   * @param string $derived_key
   *   The derived key.
   */
  public function setDerivedKey($derived_key) {
    $this->derivedKey = $derived_key;
  }

  /**
   * Derive a key for the solr hmac using the information shared with
   * acquia.com.
   *
   * @see _acquia_search_derived_key()
   */
  public function getDerivedKey() {
    if (!isset($this->derivedKey)) {
      $key = acquia_agent_settings('acquia_key');
      $subscription = acquia_agent_settings('acquia_subscription_data');
      $identifier = acquia_agent_settings('acquia_identifier');

      // We use a salt from acquia.com in key derivation since this is a shared
      // value that we could change on the AN side if needed to force any
      // or all clients to use a new derived key.  We also use a string
      // ('solr') specific to the service, since we want each service using a
      // derived key to have a separate one.
      if (empty($subscription['active']) || empty($key) || empty($identifier)) {

        // Expired or invalid subscription - don't continue.
        $this->derivedKey = '';
      }
      else {
        $salt = isset($subscription['derived_key_salt']) ? $subscription['derived_key_salt'] : '';
        $derivation_string = $identifier . 'solr' . $salt;
        $this->derivedKey = hash_hmac('sha1', str_pad($derivation_string, 80, $derivation_string), $key);
      }
    }
    return $this->derivedKey;
  }

  /**
   * Modify the url and add headers appropriate to authenticate to Acquia Search.
   *
   * @return
   *  The nonce used in the request.
   */
  public function prepareRequest(&$url, &$options, $use_data = TRUE) {

    // Add a unique request ID to the URL.
    $id = uniqid();
    if (!stristr($url, '?')) {
      $url .= "?";
    }
    else {
      $url .= "&";
    }
    $url .= 'request_id=' . $id;

    // If we're hosted on Acquia, and have an Acquia request ID,
    // append it to the request so that we map Solr queries to Acquia search requests.
    if (isset($_ENV['HTTP_X_REQUEST_ID'])) {
      $xid = empty($_ENV['HTTP_X_REQUEST_ID']) ? '-' : $_ENV['HTTP_X_REQUEST_ID'];
      $url .= '&x-request-id=' . rawurlencode($xid);
    }
    if ($use_data && isset($options['data'])) {
      list($cookie, $nonce) = $this
        ->authCookie($url, $options['data']);
    }
    else {
      list($cookie, $nonce) = $this
        ->authCookie($url);
    }
    if (empty($cookie)) {
      throw new Exception('Invalid authentication string - subscription keys expired or missing.');
    }
    $options['headers']['Cookie'] = $cookie;
    $options['headers'] += array(
      'User-Agent' => 'search_api_acquia/' . variable_get('search_api_acquia_version', '7.x'),
    );
    $options['context'] = acquia_agent_stream_context_create($url, 'acquia_search');
    if (!$options['context']) {
      throw new Exception(t("Could not create stream context"));
    }
    return $nonce;
  }

  /**
   * Validate the hmac for the response body.
   *
   * @return
   *  The response object.
   */
  public function authenticateResponse($response, $nonce, $url) {
    $hmac = $this
      ->extractHmac($response->headers);
    if (!$this
      ->validResponse($hmac, $nonce, $response->data)) {
      throw new Exception('Authentication of search content failed url: ' . $url);
    }
    return $response;
  }

  /**
   * Look in the headers and get the hmac_digest out.
   *
   * @see acquia_search_extract_hmac()
   */
  protected function extractHmac($headers) {
    $reg = array();
    if (is_array($headers)) {
      foreach ($headers as $name => $value) {
        if (strtolower($name) == 'pragma' && preg_match("/hmac_digest=([^;]+);/i", $value, $reg)) {
          return trim($reg[1]);
        }
      }
    }
    return '';
  }

  /**
   * Validate the authenticity of returned data using a nonce and HMAC-SHA1.
   *
   * @return boolean
   *  TRUE or FALSE depending on whether the response is valid.
   *
   * @see acquia_search_valid_response()
   */
  protected function validResponse($hmac, $nonce, $string, $derived_key = NULL) {
    if (empty($derived_key)) {
      $derived_key = $this
        ->getDerivedKey();
    }
    return $hmac == hash_hmac('sha1', $nonce . $string, $derived_key);
  }

  /**
   * Make a request to a servlet (a path) that's not a standard path.
   *
   * @override
   */
  public function makeServletRequest($servlet, array $params = array(), array $options = array()) {

    // Add default params.
    $params += array(
      'wt' => 'json',
    );
    $url = $this
      ->constructUrl($servlet, $params);

    // We assume we only authenticate the URL for other servlets.
    $nonce = $this
      ->prepareRequest($url, $options, FALSE);
    $response = $this
      ->makeHttpRequest($url, $options);
    $response = $this
      ->checkResponse($response);
    return $this
      ->authenticateResponse($response, $nonce, $url);
  }

  /**
   * Central method for making a GET operation against this Solr Server
   *
   * @override
   */
  protected function sendRawGet($url, array $options = array()) {
    $nonce = $this
      ->prepareRequest($url, $options);
    $response = $this
      ->makeHttpRequest($url, $options);
    $response = $this
      ->checkResponse($response);
    return $this
      ->authenticateResponse($response, $nonce, $url);
  }

  /**
   * Central method for making a POST operation against this Solr Server
   *
   * @override
   */
  protected function sendRawPost($url, array $options = array()) {
    $options['method'] = 'POST';

    // Normally we use POST to send XML documents.
    if (!isset($options['headers']['Content-Type'])) {
      $options['headers']['Content-Type'] = 'text/xml; charset=UTF-8';
    }
    $nonce = $this
      ->prepareRequest($url, $options);
    $response = $this
      ->makeHttpRequest($url, $options);
    $response = $this
      ->checkResponse($response);
    return $this
      ->authenticateResponse($response, $nonce, $url);
  }

  /**
   * Modify a solr base url and construct a hmac authenticator cookie.
   *
   * @param $url
   *  The solr url beng requested - passed by reference and may be altered.
   * @param $string
   *  A string - the data to be authenticated, or empty to just use the path
   *  and query from the url to build the authenticator.
   * @param $derived_key
   *  Optional string to supply the derived key.
   *
   * @return
   *  An array containing the string to be added as the content of the
   *  Cookie header to the request and the nonce.
   *
   * @see acquia_search_auth_cookie
   */
  function authCookie(&$url, $string = '', $derived_key = NULL) {
    $uri = parse_url($url);

    // Add a scheme - should always be https if available.
    if (in_array('ssl', stream_get_transports(), TRUE) && !defined('ACQUIA_DEVELOPMENT_NOSSL')) {
      $scheme = 'https://';
      $port = '';
    }
    else {
      $scheme = 'http://';
      $port = isset($uri['port']) && $uri['port'] != 80 ? ':' . $uri['port'] : '';
    }
    $path = isset($uri['path']) ? $uri['path'] : '/';
    $query = isset($uri['query']) ? '?' . $uri['query'] : '';
    $url = $scheme . $uri['host'] . $port . $path . $query;

    // 32 character nonce.
    $nonce = base64_encode(drupal_random_bytes(24));
    if ($string) {
      $auth_header = $this
        ->authenticator($string, $nonce, $derived_key);
    }
    else {
      $auth_header = $this
        ->authenticator($path . $query, $nonce, $derived_key);
    }
    return array(
      $auth_header,
      $nonce,
    );
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SearchApiAcquiaSearchConnection::$derivedKey protected property The derived key used to HMAC hash the search request.
SearchApiAcquiaSearchConnection::authCookie function Modify a solr base url and construct a hmac authenticator cookie.
SearchApiAcquiaSearchConnection::authenticateResponse public function Validate the hmac for the response body.
SearchApiAcquiaSearchConnection::authenticator function Creates an authenticator based on a data string and HMAC-SHA1.
SearchApiAcquiaSearchConnection::extractHmac protected function Look in the headers and get the hmac_digest out.
SearchApiAcquiaSearchConnection::getDerivedKey public function Derive a key for the solr hmac using the information shared with acquia.com.
SearchApiAcquiaSearchConnection::makeServletRequest public function Make a request to a servlet (a path) that's not a standard path. Overrides SearchApiSolrConnection::makeServletRequest
SearchApiAcquiaSearchConnection::prepareRequest public function Modify the url and add headers appropriate to authenticate to Acquia Search.
SearchApiAcquiaSearchConnection::sendRawGet protected function Central method for making a GET operation against this Solr Server Overrides SearchApiSolrConnection::sendRawGet
SearchApiAcquiaSearchConnection::sendRawPost protected function Central method for making a POST operation against this Solr Server Overrides SearchApiSolrConnection::sendRawPost
SearchApiAcquiaSearchConnection::setDerivedKey public function Sets the derived key used to HMAC hash the search request.
SearchApiAcquiaSearchConnection::validResponse protected function Validate the authenticity of returned data using a nonce and HMAC-SHA1.
SearchApiSolrConnection::$base_url protected property The Solr server's URL.
SearchApiSolrConnection::$http_auth protected property HTTP Basic Authentication header to set for requests to the Solr server.
SearchApiSolrConnection::$luke protected property Cache for the metadata from admin/luke.
SearchApiSolrConnection::$options protected property The options passed when creating this connection.
SearchApiSolrConnection::$soft_commit protected property Flag that denotes whether to use soft commits for Solr 4.x.
SearchApiSolrConnection::$stats protected property Cache for information about the Solr core.
SearchApiSolrConnection::$stream_context protected property The stream context to use for requests to the Solr server.
SearchApiSolrConnection::$system_info protected property Cache for system information.
SearchApiSolrConnection::$update_url protected property Cached URL to the update servlet.
SearchApiSolrConnection::addDocuments public function Adds an array of Solr Documents to the index all at once Overrides SearchApiSolrConnectionInterface::addDocuments
SearchApiSolrConnection::checkResponse protected function Checks the reponse code and throws an exception if it's not 200.
SearchApiSolrConnection::clearCache public function Clears the cached Solr data. Overrides SearchApiSolrConnectionInterface::clearCache
SearchApiSolrConnection::commit public function Sends a commit command to the Solr server. Overrides SearchApiSolrConnectionInterface::commit
SearchApiSolrConnection::constructUrl protected function Returns the HTTP URL for a certain servlet on the Solr server.
SearchApiSolrConnection::deleteById public function Sends a delete request based on a document ID. Overrides SearchApiSolrConnectionInterface::deleteById
SearchApiSolrConnection::deleteByMultipleIds public function Sends a delete request for several documents, based on the document IDs. Overrides SearchApiSolrConnectionInterface::deleteByMultipleIds
SearchApiSolrConnection::deleteByQuery public function Sends a delete request for all documents that match the given Solr query. Overrides SearchApiSolrConnectionInterface::deleteByQuery
SearchApiSolrConnection::escape public static function Escapes special characters from a Solr query. Overrides SearchApiSolrConnectionInterface::escape
SearchApiSolrConnection::escapeFieldName public static function Escapes a Search API field name for passing to Solr. Overrides SearchApiSolrConnectionInterface::escapeFieldName
SearchApiSolrConnection::escapePhrase public static function Escapes a string that should be included in a Solr phrase. Overrides SearchApiSolrConnectionInterface::escapePhrase
SearchApiSolrConnection::FILE_SERVLET constant Path to the file servlet.
SearchApiSolrConnection::getBaseUrl public function Gets the base URL of the Solr server. Overrides SearchApiSolrConnectionInterface::getBaseUrl
SearchApiSolrConnection::getCacheId protected function Computes the cache ID to use for this connection.
SearchApiSolrConnection::getFields public function Get metadata about fields in the Solr/Lucene index. Overrides SearchApiSolrConnectionInterface::getFields
SearchApiSolrConnection::getLuke public function Gets meta-data about the index. Overrides SearchApiSolrConnectionInterface::getLuke
SearchApiSolrConnection::getSoftCommit public function Tells whether this connection will use soft commits when comitting. Overrides SearchApiSolrConnectionInterface::getSoftCommit
SearchApiSolrConnection::getSolrVersion public function Gets the current solr version. Overrides SearchApiSolrConnectionInterface::getSolrVersion
SearchApiSolrConnection::getStats public function Gets information about the Solr core. Overrides SearchApiSolrConnectionInterface::getStats
SearchApiSolrConnection::getStatsSummary public function Gets summary information about the Solr Core. Overrides SearchApiSolrConnectionInterface::getStatsSummary
SearchApiSolrConnection::getStreamContext public function Returns the stream context to use for requests to the Solr server. Overrides SearchApiSolrConnectionInterface::getStreamContext
SearchApiSolrConnection::getSystemInfo public function Implements SearchApiSolrConnectionInterface::getSystemInfo(). Overrides SearchApiSolrConnectionInterface::getSystemInfo
SearchApiSolrConnection::httpBuildQuery protected function Generates an URL-encoded query string.
SearchApiSolrConnection::logResponse protected function Logs a Solr response object.
SearchApiSolrConnection::LUKE_SERVLET constant Path to the luke servlet.
SearchApiSolrConnection::makeHttpRequest protected function Sends an HTTP request to Solr.
SearchApiSolrConnection::NAMED_LIST_FORMAT constant Defines how NamedLists should be formatted in the output.
SearchApiSolrConnection::optimize public function Sends an optimize command to the Solr server. Overrides SearchApiSolrConnectionInterface::optimize
SearchApiSolrConnection::optimizeOrCommit protected function Sends a commit or optimize command to the Solr server.
SearchApiSolrConnection::phrase public static function Converts a string to a Solr phrase. Overrides SearchApiSolrConnectionInterface::phrase
SearchApiSolrConnection::ping public function Calls the /admin/ping servlet, to test the connection to the server. Overrides SearchApiSolrConnectionInterface::ping
SearchApiSolrConnection::PING_SERVLET constant Path to the ping servlet.
SearchApiSolrConnection::search public function Executes a search on the Solr server. Overrides SearchApiSolrConnectionInterface::search
SearchApiSolrConnection::SEARCH_SERVLET constant Path to the search servlet.
SearchApiSolrConnection::setBaseUrl public function Sets the base URL of the Solr server. Overrides SearchApiSolrConnectionInterface::setBaseUrl
SearchApiSolrConnection::setLuke protected function Sets $this->luke with the metadata about the index from admin/luke.
SearchApiSolrConnection::setSoftCommit public function Sets whether this connection will use soft commits when comitting. Overrides SearchApiSolrConnectionInterface::setSoftCommit
SearchApiSolrConnection::setStats protected function Stores information about the Solr core in $this->stats.
SearchApiSolrConnection::setStreamContext public function Set the stream context to use for requests to the Solr server. Overrides SearchApiSolrConnectionInterface::setStreamContext
SearchApiSolrConnection::setSystemInfo protected function Call the /admin/system servlet to retrieve system information.
SearchApiSolrConnection::STATS_SERVLET constant Path to the stats servlet.
SearchApiSolrConnection::STATS_SERVLET_4 constant Path to the stats servlet for Solr 4.x servers.
SearchApiSolrConnection::SYSTEM_SERVLET constant Path to the system servlet.
SearchApiSolrConnection::update public function Sends a raw update request to the Solr server. Overrides SearchApiSolrConnectionInterface::update
SearchApiSolrConnection::UPDATE_SERVLET constant Path to the update servlet.
SearchApiSolrConnection::__construct public function Implements SearchApiSolrConnectionInterface::__construct(). Overrides SearchApiSolrConnectionInterface::__construct