Skip to content

Commit

Permalink
Added optional headers to the AWS SigningDecorator.
Browse files Browse the repository at this point in the history
Signed-off-by: dblock <[email protected]>
  • Loading branch information
dblock committed Jan 10, 2025
1 parent 887df5e commit 6e344f3
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 5 deletions.
2 changes: 2 additions & 0 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,6 @@ $client = \OpenSearch\ClientBuilder::fromConfig($config);

## Advanced Features

* [Authentication](guides/auth.md)
* [ML Commons](guides/ml-commons.md)
* [Sending Raw JSON Requests](guides/raw-request.md)
124 changes: 124 additions & 0 deletions guides/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
- [Authentication](#authentication)
- [Basic Auth](#basic-auth)
- [Using `\OpenSearch\ClientBuilder`](#using-opensearchclientbuilder)
- [Using a Psr Client](#using-a-psr-client)
- [IAM Authentication](#iam-authentication)
- [Using `\OpenSearch\ClientBuilder`](#using-opensearchclientbuilder-1)
- [Using a Psr Client](#using-a-psr-client-1)

# Authentication

OpenSearch allows you to use different methods for the authentication.

## Basic Auth

```php
$endpoint = "https://localhost:9200"
$username = "admin"
$password = "..."
```

### Using `\OpenSearch\ClientBuilder`

```php
$client = (new \OpenSearch\ClientBuilder())
->setBasicAuthentication($username, $password)
->setHosts([$endpoint])
->setSSLVerification(false) // for testing only
->build();
```

### Using a Psr Client

```php
$symfonyPsr18Client = (new \Symfony\Component\HttpClient\Psr18Client())->withOptions([
'base_uri' => $endpoint,
'auth_basic' => [$username, $password],
'verify_peer' => false, // for testing only
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
]);
```

## IAM Authentication

This library supports IAM-based authentication when communicating with OpenSearch clusters running in Amazon Managed OpenSearch and OpenSearch Serverless.

```php
$endpoint = "https://search-....us-west-2.es.amazonaws.com"
$region = "us-west-2"
$service = "es"
$aws_access_key_id = ...
$aws_secret_access_key = ...
$aws_session_token = ...
```

### Using `\OpenSearch\ClientBuilder`

```php
$client = (new \OpenSearch\ClientBuilder())
->setHosts([$endpoint])
->setSigV4Region($region)
->setSigV4Service('es')
->setSigV4CredentialProvider([
'key' => $aws_access_key_id,
'secret' => $aws_secret_access_key,
'token' => $aws_session_token
])
->build();
```

### Using a Psr Client

```php
$symfonyPsr18Client = (new \Symfony\Component\HttpClient\Psr18Client())->withOptions([
'base_uri' => $endpoint,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
]);

$serializer = new \OpenSearch\Serializers\SmartSerializer();
$endpointFactory = new \OpenSearch\EndpointFactory();

$signer = new Aws\Signature\SignatureV4(
$service,
$region
);

$credentials = new Aws\Credentials\Credentials(
$aws_access_key_id,
$aws_secret_access_key,
$aws_session_token
);

$signingClient = new \OpenSearch\Aws\SigningClientDecorator(
$symfonyPsr18Client,
$credentials,
$signer,
[
'Host' => parse_url(getenv("ENDPOINT"))['host']
]
);

$requestFactory = new \OpenSearch\RequestFactory(
$symfonyPsr18Client,
$symfonyPsr18Client,
$symfonyPsr18Client,
$serializer,
);

$transport = (new \OpenSearch\TransportFactory())
->setHttpClient($signingClient)
->setRequestFactory($requestFactory)
->create();

$client = new \OpenSearch\Client(
$transport,
$endpointFactory,
[]
);
```
6 changes: 3 additions & 3 deletions guides/raw-request.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Raw JSON Requests
# Sending Raw JSON Requests

Opensearch client implements many high-level APIs out of the box. However, there are times when you need to send a raw
OpenSearch client implements many high-level APIs out of the box. However, there are times when you need to send a raw
JSON request to the server. This can be done using the `request()` method of the client.

The `request()` method expects the following parameters:

| Parameter | Description |
|---------------|------------------------------------------------------------------------------|
| ------------- | ---------------------------------------------------------------------------- |
| `$method` | The HTTP method to use for the request, `GET`, `POST`, `PUT`, `DELETE`, etc. |
| `$uri` | The URI to send the request to, e.g. `/_search`. |
| `$attributes` | An array of request options. `body`, `params` & `options` |
Expand Down
12 changes: 12 additions & 0 deletions src/OpenSearch/Aws/SigningClientDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,27 @@
*/
class SigningClientDecorator implements ClientInterface
{
/**
* @param ClientInterface $inner The client to decorate.
* @param CredentialsInterface $credentials The AWS credentials to use for signing requests.
* @param SignatureInterface $signer The AWS signer to use for signing requests.
* @param array $headers Additional headers to add to the request, usually `Host` is required.
* @return void
*/
public function __construct(
protected ClientInterface $inner,
protected CredentialsInterface $credentials,
protected SignatureInterface $signer,
protected array $headers = []
) {
}

public function sendRequest(RequestInterface $request): ResponseInterface
{
foreach ($this->headers as $name => $value) {
$request = $request->withHeader($name, $value);
}

$request = $request->withHeader('x-amz-content-sha256', hash('sha256', (string) $request->getBody()));
$request = $this->signer->signRequest($request, $this->credentials);
return $this->inner->sendRequest($request);
Expand Down
5 changes: 3 additions & 2 deletions tests/Aws/SigningClientDecoratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ public function testSendRequest()
->method('sendRequest')
->with(
$this->callback(function (Request $req): bool {
$this->assertEquals('localhost:9200', $req->getHeaderLine('Host'));
$this->assertEquals('server:443', $req->getHeaderLine('Host'));
$this->assertEquals('GET', $req->getMethod());
$this->assertTrue($req->hasHeader('Host'));
$this->assertTrue($req->hasHeader('Authorization'));
$this->assertTrue($req->hasHeader('x-amz-content-sha256'));
$this->assertTrue($req->hasHeader('x-amz-date'));
Expand All @@ -46,7 +47,7 @@ public function testSendRequest()
->willReturn($this->createMock(ResponseInterface::class));


$decorator = new SigningClientDecorator($client, $credentials, $signer);
$decorator = new SigningClientDecorator($client, $credentials, $signer, ['Host' => 'server:443']);
$request = new Request('GET', 'http://localhost:9200/_search');
$decorator->sendRequest($request);
}
Expand Down

0 comments on commit 6e344f3

Please sign in to comment.