Skip to content

Commit

Permalink
MDL-53412 search: Correctly handle Solr over SSL
Browse files Browse the repository at this point in the history
  • Loading branch information
ericmerrill committed Mar 17, 2016
1 parent 7adc7ef commit 5dc4624
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 37 deletions.
76 changes: 73 additions & 3 deletions search/engine/solr/classes/engine.php
Expand Up @@ -55,6 +55,11 @@ class engine extends \core_search\engine {
*/
protected $client = null;

/**
* @var \curl Direct curl object.
*/
protected $curl = null;

/**
* @var array Fields that can be highlighted.
*/
Expand Down Expand Up @@ -417,11 +422,10 @@ protected function get_search_client($triggerexception = true) {
'login' => !empty($this->config->server_username) ? $this->config->server_username : '',
'password' => !empty($this->config->server_password) ? $this->config->server_password : '',
'port' => !empty($this->config->server_port) ? $this->config->server_port : '',
'issecure' => !empty($this->config->secure) ? $this->config->secure : '',
'secure' => !empty($this->config->secure) ? true : false,
'ssl_cert' => !empty($this->config->ssl_cert) ? $this->config->ssl_cert : '',
'ssl_cert_only' => !empty($this->config->ssl_cert_only) ? $this->config->ssl_cert_only : '',
'ssl_key' => !empty($this->config->ssl_key) ? $this->config->ssl_key : '',
'ssl_password' => !empty($this->config->ssl_keypassword) ? $this->config->ssl_keypassword : '',
'ssl_keypassword' => !empty($this->config->ssl_keypassword) ? $this->config->ssl_keypassword : '',
'ssl_cainfo' => !empty($this->config->ssl_cainfo) ? $this->config->ssl_cainfo : '',
'ssl_capath' => !empty($this->config->ssl_capath) ? $this->config->ssl_capath : '',
);
Expand All @@ -434,4 +438,70 @@ protected function get_search_client($triggerexception = true) {

return $this->client;
}

/**
* Returns a curl object for conntecting to solr.
*
* @return \curl
*/
public function get_curl_object() {
if (!is_null($this->curl)) {
return $this->curl;
}

$this->curl = new \curl();

$options = array();
// Build the SSL options. Based on pecl-solr and general testing.
if (!empty($this->config->secure)) {
if (!empty($this->config->ssl_cert)) {
$options['CURLOPT_SSLCERT'] = $this->config->ssl_cert;
$options['CURLOPT_SSLCERTTYPE'] = 'PEM';
}

if (!empty($this->config->ssl_key)) {
$options['CURLOPT_SSLKEY'] = $this->config->ssl_key;
$options['CURLOPT_SSLKEYTYPE'] = 'PEM';
}

if (!empty($this->config->ssl_keypassword)) {
$options['CURLOPT_KEYPASSWD'] = $this->config->ssl_keypassword;
}

if (!empty($this->config->ssl_cainfo)) {
$options['CURLOPT_CAINFO'] = $this->config->ssl_cainfo;
}

if (!empty($this->config->ssl_capath)) {
$options['CURLOPT_CAPATH'] = $this->config->ssl_capath;
}
}

$this->curl->setopt($options);

if (!empty($this->config->server_username) && !empty($this->config->server_password)) {
$authorization = $this->config->server_username . ':' . $this->config->server_password;
$this->curl->setHeader('Authorization', 'Basic ' . base64_encode($authorization));
}

return $this->curl;
}

/**
* Return a Moodle url object for the server connection.
*
* @param string $path The solr path to append.
* @return \moodle_url
*/
public function get_connection_url($path) {
// Must use the proper protocol, or SSL will fail.
$protocol = !empty($this->config->secure) ? 'https' : 'http';
$url = $protocol . '://' . rtrim($this->config->server_hostname, '/');
if (!empty($this->config->server_port)) {
$url .= ':' . $this->config->server_port;
}
$url .= '/solr/' . $this->config->indexname . '/' . ltrim($path, '/');

return new \moodle_url($url);
}
}
38 changes: 12 additions & 26 deletions search/engine/solr/classes/schema.php
Expand Up @@ -52,16 +52,10 @@ class schema {
protected $curl = null;

/**
* The URL.
* @var string
* An engine instance.
* @var engine
*/
protected $url = null;

/**
* The schema URL.
* @var string
*/
protected $schemaurl = null;
protected $engine = null;

/**
* Constructor.
Expand All @@ -78,23 +72,11 @@ public function __construct() {
throw new \moodle_exception('missingconfig', 'search_solr');
}

$this->curl = new \curl();
$this->engine = new engine();
$this->curl = $this->engine->get_curl_object();

// HTTP headers.
$this->curl->setHeader('Content-type: application/json');
if (!empty($this->config->server_username) && !empty($this->config->server_password)) {
$authorization = $this->config->server_username . ':' . $this->config->server_password;
$this->curl->setHeader('Authorization', 'Basic ' . base64_encode($authorization));
}

$this->url = rtrim($this->config->server_hostname, '/');
if (!empty($this->config->server_port)) {
$this->url .= ':' . $this->config->server_port;
}
$this->url .= '/solr/' . $this->config->indexname;
$this->schemaurl = $this->url . '/schema';


}

/**
Expand Down Expand Up @@ -139,7 +121,8 @@ public function validate_setup() {
protected function check_index() {

// Check that the server is available and the index exists.
$result = $this->curl->get($this->url . '/select?wt=json');
$url = $this->engine->get_connection_url('/select?wt=json');
$result = $this->curl->get($url);
if ($this->curl->error) {
throw new \moodle_exception('connectionerror', 'search_solr');
}
Expand Down Expand Up @@ -167,6 +150,8 @@ protected function add_fields($fields, $checkexisting = true) {
$this->validate_fields($fields, false);
}

$url = $this->engine->get_connection_url('/schema');

// Add all fields.
foreach ($fields as $fieldname => $data) {

Expand All @@ -183,7 +168,7 @@ protected function add_fields($fields, $checkexisting = true) {
'indexed' => $data['indexed']
)
);
$results = $this->curl->post($this->schemaurl, json_encode($params));
$results = $this->curl->post($url, json_encode($params));

// We only validate if we are interested on it.
if ($checkexisting) {
Expand All @@ -209,7 +194,8 @@ protected function validate_fields(&$fields, $requireexisting = false) {
global $CFG;

foreach ($fields as $fieldname => $data) {
$results = $this->curl->get($this->schemaurl . '/fields/' . $fieldname);
$url = $this->engine->get_connection_url('/schema/fields/' . $fieldname);
$results = $this->curl->get($url);

if ($this->curl->error) {
throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $this->curl->error);
Expand Down
8 changes: 3 additions & 5 deletions search/engine/solr/lang/en/search_solr.php
Expand Up @@ -63,11 +63,9 @@
$string['solrsslcainfo_desc'] = 'File name holding one or more CA certificates to verify peer with';
$string['solrsslcapath'] = 'SSL CA certificates path';
$string['solrsslcapath_desc'] = 'Directory path holding multiple CA certificates to verify peer with';
$string['solrsslcert'] = 'SSL key & certificate';
$string['solrsslcert_desc'] = 'File name to a PEM-formatted private key + private certificate (concatenated in that order)';
$string['solrsslcertonly'] = 'SSL certificate';
$string['solrsslcertonly_desc'] = 'File name to a PEM-formatted private certificate only';
$string['solrsslcert'] = 'SSL certificate';
$string['solrsslcert_desc'] = 'File name to a PEM-formatted private certificate';
$string['solrsslkey'] = 'SSL key';
$string['solrsslkey_desc'] = 'File name to a PEM-formatted private key';
$string['solrsslkeypassword'] = 'SSL Key password';
$string['solrsslkeypassword'] = 'SSL key password';
$string['solrsslkeypassword_desc'] = 'Password for PEM-formatted private key file';
1 change: 0 additions & 1 deletion search/engine/solr/settings.php
Expand Up @@ -42,7 +42,6 @@
$settings->add(new admin_setting_configtext('search_solr/server_password', new lang_string('solrauthpassword', 'search_solr'), '', '', PARAM_RAW));
$settings->add(new admin_setting_configtext('search_solr/server_timeout', new lang_string('solrhttpconnectiontimeout', 'search_solr'), new lang_string('solrhttpconnectiontimeout_desc', 'search_solr'), 30, PARAM_INT));
$settings->add(new admin_setting_configtext('search_solr/ssl_cert', new lang_string('solrsslcert', 'search_solr'), new lang_string('solrsslcert_desc', 'search_solr'), '', PARAM_RAW));
$settings->add(new admin_setting_configtext('search_solr/ssl_cert_only', new lang_string('solrsslcertonly', 'search_solr'), new lang_string('solrsslcertonly_desc', 'search_solr'), '', PARAM_RAW));
$settings->add(new admin_setting_configtext('search_solr/ssl_key', new lang_string('solrsslkey', 'search_solr'), new lang_string('solrsslkey_desc', 'search_solr'), '', PARAM_RAW));
$settings->add(new admin_setting_configtext('search_solr/ssl_keypassword', new lang_string('solrsslkeypassword', 'search_solr'), new lang_string('solrsslkeypassword_desc', 'search_solr'), '', PARAM_RAW));
$settings->add(new admin_setting_configtext('search_solr/ssl_cainfo', new lang_string('solrsslcainfo', 'search_solr'), new lang_string('solrsslcainfo_desc', 'search_solr'), '', PARAM_RAW));
Expand Down
26 changes: 24 additions & 2 deletions search/engine/solr/tests/engine_test.php
Expand Up @@ -25,6 +25,10 @@
* Optional params:
* - define('TEST_SEARCH_SOLR_USERNAME', '');
* - define('TEST_SEARCH_SOLR_PASSWORD', '');
* - define('TEST_SEARCH_SOLR_SSLCERT', '');
* - define('TEST_SEARCH_SOLR_SSLKEY', '');
* - define('TEST_SEARCH_SOLR_KEYPASSWORD', '');
* - define('TEST_SEARCH_SOLR_CAINFOCERT', '');
*
* @package core_search
* @category phpunit
Expand Down Expand Up @@ -71,13 +75,31 @@ public function setUp() {
set_config('indexname', TEST_SEARCH_SOLR_INDEXNAME, 'search_solr');

if (defined('TEST_SEARCH_SOLR_USERNAME')) {
set_config('server_username', TEST_SEARCH_SOLR_USERNAME);
set_config('server_username', TEST_SEARCH_SOLR_USERNAME, 'search_solr');
}

if (defined('TEST_SEARCH_SOLR_PASSWORD')) {
set_config('server_password', TEST_SEARCH_SOLR_PASSWORD);
set_config('server_password', TEST_SEARCH_SOLR_PASSWORD, 'search_solr');
}

if (defined('TEST_SEARCH_SOLR_SSLCERT')) {
set_config('secure', true, 'search_solr');
set_config('ssl_cert', TEST_SEARCH_SOLR_SSLCERT, 'search_solr');
}

if (defined('TEST_SEARCH_SOLR_SSLKEY')) {
set_config('ssl_key', TEST_SEARCH_SOLR_SSLKEY, 'search_solr');
}

if (defined('TEST_SEARCH_SOLR_KEYPASSWORD')) {
set_config('ssl_keypassword', TEST_SEARCH_SOLR_KEYPASSWORD, 'search_solr');
}

if (defined('TEST_SEARCH_SOLR_CAINFOCERT')) {
set_config('ssl_cainfo', TEST_SEARCH_SOLR_CAINFOCERT, 'search_solr');
}


// Inject search solr engine into the testable core search as we need to add the mock
// search component to it.
$searchengine = new \search_solr\engine();
Expand Down

0 comments on commit 5dc4624

Please sign in to comment.