Skip to content

Commit 8c27606

Browse files
hotfix31soyuka
andauthored
feat(elasticsearch): add SSL options for Elasticsearch configuration (#7450)
Co-authored-by: Antoine Bluchet <[email protected]>
1 parent 6f5d414 commit 8c27606

File tree

6 files changed

+178
-0
lines changed

6 files changed

+178
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Features
66

7+
* [21aa2572d](https://github.com/api-platform/core/commit/21aa2572d8fef2b3f05f7307c51348a6c9767e45) feat(elasticsearch): add SSL options for Elasticsearch configuration (#4059)
78
* [ba0c76c](https://github.com/api-platform/core/commit/ba0c76c6f5c8afa8622e87a155b8b99f453d6453) feat(doctrine): remove put & path for readonly entity (#7019)
89

910
## v4.2.2

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,8 @@ private function registerElasticsearchConfiguration(ContainerBuilder $container,
907907
$container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
908908
->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
909909
$container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
910+
$container->setParameter('api_platform.elasticsearch.ssl_ca_bundle', $config['elasticsearch']['ssl_ca_bundle']);
911+
$container->setParameter('api_platform.elasticsearch.ssl_verification', $config['elasticsearch']['ssl_verification']);
910912
$loader->load('elasticsearch.php');
911913
}
912914

src/Symfony/Bundle/DependencyInjection/Compiler/ElasticsearchClientPass.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ public function process(ContainerBuilder $container): void
5656
}
5757
}
5858

59+
if ($sslCaBundle = $container->getParameter('api_platform.elasticsearch.ssl_ca_bundle')) {
60+
$clientConfiguration['CABundle'] = $sslCaBundle;
61+
}
62+
63+
if (false === $container->getParameter('api_platform.elasticsearch.ssl_verification')) {
64+
$clientConfiguration['SSLVerification'] = false;
65+
}
66+
5967
$clientDefinition = $container->getDefinition('api_platform.elasticsearch.client');
6068

6169
if (!$clientConfiguration) {

src/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,10 @@ private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
473473
->arrayNode('elasticsearch')
474474
->canBeEnabled()
475475
->addDefaultsIfNotSet()
476+
->validate()
477+
->ifTrue(static fn (array $v): bool => null !== ($v['ssl_ca_bundle'] ?? null) && false === ($v['ssl_verification'] ?? true))
478+
->thenInvalid('The "ssl_ca_bundle" and "ssl_verification: false" options cannot be used together. Either provide a CA bundle path or disable SSL verification, not both.')
479+
->end()
476480
->children()
477481
->booleanNode('enabled')
478482
->defaultFalse()
@@ -497,6 +501,14 @@ private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
497501
->defaultValue([])
498502
->prototype('scalar')->end()
499503
->end()
504+
->scalarNode('ssl_ca_bundle')
505+
->defaultNull()
506+
->info('Path to the SSL CA bundle file for Elasticsearch SSL verification.')
507+
->end()
508+
->booleanNode('ssl_verification')
509+
->defaultTrue()
510+
->info('Enable or disable SSL verification for Elasticsearch connections.')
511+
->end()
500512
->end()
501513
->end()
502514
->end();

tests/Symfony/Bundle/DependencyInjection/Compiler/ElasticsearchClientPassTest.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public function testProcess(): void
6969
$containerBuilderProphecy = $this->prophesize(ContainerBuilder::class);
7070
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.enabled')->willReturn(true)->shouldBeCalled();
7171
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.hosts')->willReturn(['http://localhost:9200'])->shouldBeCalled();
72+
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.ssl_ca_bundle')->willReturn(null)->shouldBeCalled();
73+
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.ssl_verification')->willReturn(true)->shouldBeCalled();
7274
$containerBuilderProphecy->has('logger')->willReturn(true)->shouldBeCalled();
7375
$containerBuilderProphecy->getDefinition('api_platform.elasticsearch.client')->willReturn($clientDefinitionProphecy->reveal())->shouldBeCalled();
7476

@@ -89,6 +91,8 @@ public function testProcessWithoutConfiguration(): void
8991
$containerBuilderProphecy = $this->prophesize(ContainerBuilder::class);
9092
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.enabled')->willReturn(true)->shouldBeCalled();
9193
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.hosts')->willReturn([])->shouldBeCalled();
94+
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.ssl_ca_bundle')->willReturn(null)->shouldBeCalled();
95+
$containerBuilderProphecy->getParameter('api_platform.elasticsearch.ssl_verification')->willReturn(true)->shouldBeCalled();
9296
$containerBuilderProphecy->has('logger')->willReturn(false)->shouldBeCalled();
9397
$containerBuilderProphecy->getDefinition('api_platform.elasticsearch.client')->willReturn($clientDefinitionProphecy->reveal())->shouldBeCalled();
9498

@@ -102,4 +106,102 @@ public function testProcessWithElasticsearchDisabled(): void
102106

103107
(new ElasticsearchClientPass())->process($containerBuilderProphecy->reveal());
104108
}
109+
110+
public function testProcessWithSslCaBundle(): void
111+
{
112+
$clientBuilder = class_exists(\Elasticsearch\ClientBuilder::class)
113+
// ES v7
114+
? \Elasticsearch\ClientBuilder::class
115+
// ES v8 and up
116+
: \Elastic\Elasticsearch\ClientBuilder::class;
117+
118+
$clientDefinition = $this->createMock(Definition::class);
119+
$clientDefinition->expects($this->once())
120+
->method('setFactory')
121+
->with([$clientBuilder, 'fromConfig'])
122+
->willReturnSelf();
123+
124+
$clientDefinition->expects($this->once())
125+
->method('setArguments')
126+
->with($this->callback(function ($arguments) {
127+
$config = $arguments[0];
128+
129+
return isset($config['hosts'])
130+
&& $config['hosts'] === ['https://localhost:9200']
131+
&& isset($config['CABundle'])
132+
&& '/path/to/ca-bundle.crt' === $config['CABundle']
133+
&& isset($config['logger'])
134+
&& $config['logger'] instanceof Reference;
135+
}))
136+
->willReturnSelf();
137+
138+
$containerBuilder = $this->createMock(ContainerBuilder::class);
139+
$containerBuilder->method('getParameter')
140+
->willReturnMap([
141+
['api_platform.elasticsearch.enabled', true],
142+
['api_platform.elasticsearch.hosts', ['https://localhost:9200']],
143+
['api_platform.elasticsearch.ssl_ca_bundle', '/path/to/ca-bundle.crt'],
144+
['api_platform.elasticsearch.ssl_verification', true],
145+
]);
146+
147+
$containerBuilder->expects($this->once())
148+
->method('has')
149+
->with('logger')
150+
->willReturn(true);
151+
152+
$containerBuilder->expects($this->once())
153+
->method('getDefinition')
154+
->with('api_platform.elasticsearch.client')
155+
->willReturn($clientDefinition);
156+
157+
(new ElasticsearchClientPass())->process($containerBuilder);
158+
}
159+
160+
public function testProcessWithSslVerificationDisabled(): void
161+
{
162+
$clientBuilder = class_exists(\Elasticsearch\ClientBuilder::class)
163+
? \Elasticsearch\ClientBuilder::class
164+
: \Elastic\Elasticsearch\ClientBuilder::class;
165+
166+
$clientDefinition = $this->createMock(Definition::class);
167+
$clientDefinition->expects($this->once())
168+
->method('setFactory')
169+
->with([$clientBuilder, 'fromConfig'])
170+
->willReturnSelf();
171+
172+
$clientDefinition->expects($this->once())
173+
->method('setArguments')
174+
->with($this->callback(function ($arguments) {
175+
$config = $arguments[0];
176+
177+
return isset($config['hosts'])
178+
&& $config['hosts'] === ['https://localhost:9200']
179+
&& isset($config['SSLVerification'])
180+
&& false === $config['SSLVerification']
181+
&& isset($config['logger'])
182+
&& $config['logger'] instanceof Reference;
183+
}))
184+
->willReturnSelf();
185+
186+
$containerBuilder = $this->createMock(ContainerBuilder::class);
187+
$containerBuilder->method('getParameter')
188+
->willReturnMap([
189+
['api_platform.elasticsearch.enabled', true],
190+
['api_platform.elasticsearch.hosts', ['https://localhost:9200']],
191+
['api_platform.elasticsearch.ssl_ca_bundle', null],
192+
['api_platform.elasticsearch.ssl_verification', false],
193+
]);
194+
195+
$containerBuilder->expects($this->once())
196+
->method('has')
197+
->with('logger')
198+
->willReturn(true);
199+
200+
$containerBuilder->expects($this->once())
201+
->method('getDefinition')
202+
->with('api_platform.elasticsearch.client')
203+
->willReturn($clientDefinition);
204+
205+
(new ElasticsearchClientPass())->process($containerBuilder);
206+
}
105207
}

tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
135135
'elasticsearch' => [
136136
'enabled' => false,
137137
'hosts' => [],
138+
'ssl_ca_bundle' => null,
139+
'ssl_verification' => true,
138140
],
139141
'oauth' => [
140142
'enabled' => false,
@@ -439,4 +441,55 @@ public function testOpenApiTags(): void
439441

440442
$this->assertEquals(['name' => 'test3', 'description' => null], $config['openapi']['tags'][1]);
441443
}
444+
445+
public function testElasticsearchSslCaBundleConfiguration(): void
446+
{
447+
$config = $this->processor->processConfiguration($this->configuration, [
448+
'api_platform' => [
449+
'elasticsearch' => [
450+
'enabled' => true,
451+
'hosts' => ['https://localhost:9200'],
452+
'ssl_ca_bundle' => '/path/to/ca-bundle.crt',
453+
],
454+
],
455+
]);
456+
457+
$this->assertTrue($config['elasticsearch']['enabled']);
458+
$this->assertSame('/path/to/ca-bundle.crt', $config['elasticsearch']['ssl_ca_bundle']);
459+
$this->assertTrue($config['elasticsearch']['ssl_verification']);
460+
}
461+
462+
public function testElasticsearchSslVerificationDisabled(): void
463+
{
464+
$config = $this->processor->processConfiguration($this->configuration, [
465+
'api_platform' => [
466+
'elasticsearch' => [
467+
'enabled' => true,
468+
'hosts' => ['https://localhost:9200'],
469+
'ssl_verification' => false,
470+
],
471+
],
472+
]);
473+
474+
$this->assertTrue($config['elasticsearch']['enabled']);
475+
$this->assertFalse($config['elasticsearch']['ssl_verification']);
476+
$this->assertNull($config['elasticsearch']['ssl_ca_bundle']);
477+
}
478+
479+
public function testElasticsearchSslCaBundleAndVerificationDisabledMutuallyExclusive(): void
480+
{
481+
$this->expectException(InvalidConfigurationException::class);
482+
$this->expectExceptionMessage('The "ssl_ca_bundle" and "ssl_verification: false" options cannot be used together. Either provide a CA bundle path or disable SSL verification, not both.');
483+
484+
$this->processor->processConfiguration($this->configuration, [
485+
'api_platform' => [
486+
'elasticsearch' => [
487+
'enabled' => true,
488+
'hosts' => ['https://localhost:9200'],
489+
'ssl_ca_bundle' => '/path/to/ca-bundle.crt',
490+
'ssl_verification' => false,
491+
],
492+
],
493+
]);
494+
}
442495
}

0 commit comments

Comments
 (0)