From 6e344f3c3dbfde3af81e90a44a7932fe26480477 Mon Sep 17 00:00:00 2001 From: dblock Date: Fri, 10 Jan 2025 10:30:25 -0500 Subject: [PATCH] Added optional headers to the AWS SigningDecorator. Signed-off-by: dblock --- USER_GUIDE.md | 2 + guides/auth.md | 124 ++++++++++++++++++ guides/raw-request.md | 6 +- src/OpenSearch/Aws/SigningClientDecorator.php | 12 ++ tests/Aws/SigningClientDecoratorTest.php | 5 +- 5 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 guides/auth.md diff --git a/USER_GUIDE.md b/USER_GUIDE.md index c0c90a39..e37a2a18 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -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) diff --git a/guides/auth.md b/guides/auth.md new file mode 100644 index 00000000..5ad8bdb3 --- /dev/null +++ b/guides/auth.md @@ -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, + [] +); +``` \ No newline at end of file diff --git a/guides/raw-request.md b/guides/raw-request.md index 5424f8cf..48a28b3b 100644 --- a/guides/raw-request.md +++ b/guides/raw-request.md @@ -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` | diff --git a/src/OpenSearch/Aws/SigningClientDecorator.php b/src/OpenSearch/Aws/SigningClientDecorator.php index e54752f3..bdf2409c 100644 --- a/src/OpenSearch/Aws/SigningClientDecorator.php +++ b/src/OpenSearch/Aws/SigningClientDecorator.php @@ -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); diff --git a/tests/Aws/SigningClientDecoratorTest.php b/tests/Aws/SigningClientDecoratorTest.php index d94c335b..be5bdebb 100644 --- a/tests/Aws/SigningClientDecoratorTest.php +++ b/tests/Aws/SigningClientDecoratorTest.php @@ -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')); @@ -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); }