Skip to content

Commit 6b45b96

Browse files
committed
Merge branch 'elastic-cloud'
2 parents 990efbb + 1348fbe commit 6b45b96

File tree

9 files changed

+381
-6
lines changed

9 files changed

+381
-6
lines changed

docs/configuration.asciidoc

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,30 @@ $client = ClientBuilder::create() // Instantiate a new ClientBuilder
8888
Only the `host` parameter is required for each configured host. If not provided, the default port is `9200`. The default
8989
scheme is `http`.
9090

91+
=== Connect to Elastic Cloud
92+
93+
If you want to connect to Elastic Cloud, you can use your Cloud ID and use basic authentication or ApiKey authentication to connect to it.
94+
95+
[source,php]
96+
----
97+
$client = ClientBuilder::create()
98+
->setElasticCloudId('<elastic-cloud-id>') <1>
99+
->setBasicAuthentication('<username>', '<secure-password>') <2>
100+
->build();
101+
----
102+
<1> Your Cloud ID provided by the Elastic Cloud platform
103+
<2> Your basic authentication credentials
104+
105+
[source,php]
106+
----
107+
$client = ClientBuilder::create()
108+
->setElasticCloudId('<elastic-cloud-id>') <1>
109+
->setApiKey('<id>', '<api_key>') <2>
110+
->build();
111+
----
112+
<1> Your Cloud ID provided by the Elastic Cloud platform
113+
<2> Your ApiKey pair as described https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/security.html[here]
114+
91115
=== Authorization and Encryption
92116

93117
For details about HTTP Authorization and SSL encryption, see
@@ -244,7 +268,7 @@ $client = ClientBuilder::create()
244268
->build();
245269
----
246270

247-
For more details, please see the dedicated page on
271+
For more details, please see the dedicated page on
248272
<<connection_pool,configuring connection pools>>.
249273

250274
=== Setting the Connection Selector

docs/security.asciidoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ $client = ClientBuilder::create()
2424
Credentials are provided per-host, which allows each host to have their own set of credentials. All requests sent to the
2525
cluster will use the appropriate credentials depending on the node being talked to.
2626

27+
=== ApiKey Authentication
28+
29+
If your Elasticsearch cluster is secured by API keys as described https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[here], you can use these values to connect the client with your cluster, as illustrated in the following code snippet.
30+
31+
[source,php]
32+
----
33+
$client = ClientBuilder::create()
34+
->setApiKey('id', 'api_key') <1>
35+
->build();
36+
----
37+
<1> ApiKey pair of `id` and `api_key` from the create API key response.
38+
2739
=== SSL Encryption
2840

2941
Configuring SSL is a little more complex. You need to identify if your certificate has been signed by a public

phpstan-src-71.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ parameters:
66
- '#Variable (.*) in isset\(\) always exists and is not nullable.#'
77
- '#Constant JSON_THROW_ON_ERROR not found#'
88
- '#class JsonException#'
9+
-
10+
message: '#is not subtype of Throwable#'
11+
path: %currentWorkingDirectory%/src/Elasticsearch/ClientBuilder.php

phpstan-src.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ parameters:
44
# nullable types are missing everywhere
55
# this should be removed and fixed in the code later
66
- '#Variable (.*) in isset\(\) always exists and is not nullable.#'
7+
-
8+
message: '#is not subtype of Throwable#'
9+
path: %currentWorkingDirectory%/src/Elasticsearch/ClientBuilder.php

src/Elasticsearch/ClientBuilder.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use Elasticsearch\Common\Exceptions\InvalidArgumentException;
88
use Elasticsearch\Common\Exceptions\RuntimeException;
9+
use Elasticsearch\Common\Exceptions\ElasticCloudIdParseException;
10+
use Elasticsearch\Common\Exceptions\AuthenticationConfigException;
911
use Elasticsearch\ConnectionPool\AbstractConnectionPool;
1012
use Elasticsearch\ConnectionPool\Selectors\RoundRobinSelector;
1113
use Elasticsearch\ConnectionPool\Selectors\SelectorInterface;
@@ -330,6 +332,82 @@ public function setHosts(array $hosts): ClientBuilder
330332
return $this;
331333
}
332334

335+
/**
336+
* Set the APIKey Pair, consiting of the API Id and the ApiKey of the Response from /_security/api_key
337+
*
338+
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html
339+
*
340+
* @throws Elasticsearch\Common\Exceptions\AuthenticationConfigException
341+
*/
342+
public function setApiKey(string $id, string $apiKey): ClientBuilder
343+
{
344+
if (isset($this->connectionParams['client']['curl'][CURLOPT_HTTPAUTH]) === true) {
345+
throw new AuthenticationConfigException("You can't use APIKey - and Basic Authenication together.");
346+
}
347+
348+
$this->connectionParams['client']['headers']['Authorization'] = [
349+
'ApiKey ' . base64_encode($id . ':' . $apiKey)
350+
];
351+
352+
return $this;
353+
}
354+
355+
/**
356+
* Set the APIKey Pair, consiting of the API Id and the ApiKey of the Response from /_security/api_key
357+
*
358+
* @param string $username
359+
* @param string $password
360+
*
361+
* @throws Elasticsearch\Common\Exceptions\AuthenticationConfigException
362+
*/
363+
public function setBasicAuthentication(string $username, string $password): ClientBuilder
364+
{
365+
if (isset($this->connectionParams['client']['headers']['Authorization']) === true) {
366+
throw new AuthenticationConfigException("You can't use APIKey - and Basic Authenication together.");
367+
}
368+
369+
if (isset($this->connectionParams['client']['curl']) === false) {
370+
$this->connectionParams['client']['curl'] = [];
371+
}
372+
373+
$this->connectionParams['client']['curl'] += [
374+
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
375+
CURLOPT_USERPWD => $username.':'.$password
376+
];
377+
378+
return $this;
379+
}
380+
381+
/**
382+
* Set Elastic Cloud ID to connect to Elastic Cloud
383+
*
384+
* @link https://elastic.co/cloud
385+
*
386+
* @param string $cloudId
387+
*/
388+
public function setElasticCloudId(string $cloudId): ClientBuilder
389+
{
390+
// Register the Hosts array
391+
$this->setHosts([
392+
[
393+
'host' => $this->parseElasticCloudId($cloudId),
394+
'port' => '',
395+
'scheme' => 'https',
396+
]
397+
]);
398+
399+
// Merge best practices for the connection
400+
$this->setConnectionParams([
401+
'client' => [
402+
'curl' => [
403+
CURLOPT_ENCODING => 1,
404+
],
405+
]
406+
]);
407+
408+
return $this;
409+
}
410+
333411
public function setConnectionParams(array $params): ClientBuilder
334412
{
335413
$this->connectionParams = $params;
@@ -565,6 +643,7 @@ private function buildConnectionsFromHosts(array $hosts): array
565643
$this->logger->error("Could not parse host: ".print_r($host, true));
566644
throw new RuntimeException("Could not parse host: ".print_r($host, true));
567645
}
646+
568647
$connections[] = $this->connectionFactory->create($host);
569648
}
570649

@@ -616,4 +695,26 @@ private function prependMissingScheme(string $host): string
616695

617696
return $host;
618697
}
698+
699+
/**
700+
* Parse the Elastic Cloud Params from the CloudId
701+
*
702+
* @param string $cloudId
703+
*
704+
* @return string
705+
*
706+
* @throws ElasticCloudIdParseException
707+
*/
708+
private function parseElasticCloudId(string $cloudId): string
709+
{
710+
try {
711+
list($name, $encoded) = explode(':', $cloudId);
712+
list($uri, $uuids) = explode('$', base64_decode($encoded));
713+
list($es,) = explode(':', $uuids);
714+
715+
return $es . '.' . $uri;
716+
} catch (\Throwable $t) {
717+
throw new ElasticCloudIdParseException('could not parse the Cloud ID:' . $cloudId);
718+
}
719+
}
619720
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
// Licensed to Elasticsearch B.V under one or more agreements.
5+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
6+
// See the LICENSE file in the project root for more information
7+
8+
namespace Elasticsearch\Common\Exceptions;
9+
10+
/**
11+
* AuthenticationConfigException
12+
*
13+
* @category Elasticsearch
14+
* @package Elasticsearch\Common\Exceptions
15+
* @author Philip Krauss <[email protected]>
16+
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache2
17+
* @link http://elastic.co
18+
*/
19+
class AuthenticationConfigException extends \RuntimeException implements ElasticsearchException
20+
{
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
// Licensed to Elasticsearch B.V under one or more agreements.
4+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
5+
// See the LICENSE file in the project root for more information
6+
7+
namespace Elasticsearch\Common\Exceptions;
8+
9+
/**
10+
* RuntimeException
11+
*
12+
* @category Elasticsearch
13+
* @package Elasticsearch\Common\Exceptions
14+
* @author Philip Krauss <[email protected]>
15+
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache2
16+
* @link http://elastic.co
17+
*/
18+
class ElasticCloudIdParseException extends \RuntimeException implements ElasticsearchException
19+
{
20+
}

src/Elasticsearch/Connections/Connection.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,11 @@ public function __construct(
129129
$this->transportSchema = $hostDetails['scheme'];
130130
}
131131

132-
if (isset($hostDetails['user']) && isset($hostDetails['pass'])) {
132+
// Only Set the Basic if API Key is not set and setBasicAuthentication was not called prior
133+
if (isset($connectionParams['client']['headers']['Authorization']) === false
134+
&& isset($connectionParams['client']['curl'][CURLOPT_HTTPAUTH]) === false
135+
&& isset($hostDetails['user'])
136+
&& isset($hostDetails['pass'])) {
133137
$connectionParams['client']['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
134138
$connectionParams['client']['curl'][CURLOPT_USERPWD] = $hostDetails['user'].':'.$hostDetails['pass'];
135139
}

0 commit comments

Comments
 (0)