Skip to content

Commit 5600971

Browse files
authored
INT-192: Secure proxy endpoint
Secure proxy endpoint
1 parent a23b02d commit 5600971

3 files changed

Lines changed: 65 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
- Upgrade Web Components version to v5.1.8
55
- Add template to suggest element
66

7+
### Fix
8+
- Secure proxy endpoint
9+
710
## [v6.5.0] - 2025.08.06
811
### Add
912
- Add to cart button for record list

spec/Storefront/Controller/ProxyControllerSpec.php

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Psr\Http\Message\RequestInterface;
1919
use Psr\Http\Message\ResponseInterface;
2020
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21+
use Symfony\Component\HttpFoundation\HeaderBag;
2122
use Symfony\Component\HttpFoundation\JsonResponse;
2223
use Symfony\Component\HttpFoundation\Request;
2324
use Symfony\Component\HttpFoundation\Response;
@@ -30,21 +31,49 @@ class ProxyControllerSpec extends ObjectBehavior
3031
public function let(
3132
Communication $config,
3233
ClientInterface $client,
33-
ClientBuilder $clientBuilder
34+
ClientBuilder $clientBuilder,
35+
Request $request,
36+
HeaderBag $headerBag
3437
): void {
3538
$serverUrl = 'https://example.fact-finder.de/fact-finder';
3639
$config->getServerUrl()->willReturn($serverUrl);
3740
$config->getVersion()->willReturn('ng');
3841
$config->getApiKey()->willReturn('api-key-123');
42+
$config->isProxyEnabled()->willReturn(true);
43+
3944
$this->beConstructedWith($config);
45+
4046
$clientBuilder->build()->willReturn($client);
4147
$clientBuilder->withServerUrl(Argument::any())->willReturn($clientBuilder);
4248
$clientBuilder->withApiKey(Argument::any())->willReturn($clientBuilder);
4349
$clientBuilder->withVersion(Argument::any())->willReturn($clientBuilder);
50+
51+
$request->headers = $headerBag;
52+
$headerBag->get('x-ff-api-key')->willReturn('api-key-123');
53+
4454
$this->client = $client;
4555
$this->clientBuilder = $clientBuilder;
4656
}
4757

58+
public function it_should_return_unauthorized_if_api_key_header_is_missing(
59+
Request $request,
60+
HeaderBag $headerBag,
61+
ClientBuilder $clientBuilder,
62+
EventDispatcherInterface $eventDispatcher
63+
): void {
64+
// Given
65+
$request->headers = $headerBag;
66+
$headerBag->get('x-ff-api-key')->willReturn(null);
67+
68+
// When
69+
$response = $this->execute('some/endpoint', $request, $clientBuilder, $eventDispatcher);
70+
71+
// Then
72+
$response->shouldBeAnInstanceOf(JsonResponse::class);
73+
Assert::assertEquals(Response::HTTP_UNAUTHORIZED, $response->getWrappedObject()->getStatusCode());
74+
Assert::assertStringContainsString('UNAUTHORIZED', $response->getWrappedObject()->getContent());
75+
}
76+
4877
public function it_should_return_success_response(
4978
Request $request,
5079
ResponseInterface $response,
@@ -56,8 +85,10 @@ public function it_should_return_success_response(
5685
$request->getMethod()->willReturn(Request::METHOD_GET);
5786
$uri = 'rest/v5/search/example_channel?query=bag&sid=123&format=json';
5887
$_SERVER['REQUEST_URI'] = sprintf('/fact-finder/proxy/%s', $uri);
88+
$this->clientBuilder->withApiKey('api-key-123')->shouldBeCalled()->willReturn($this->clientBuilder);
89+
5990
$this->client->request(Request::METHOD_GET, $uri)->willReturn($response);
60-
$jsonResponse = file_get_contents(dirname(__DIR__, 2) . '/data/proxy/search-bag.json');
91+
$jsonResponse = '{"results": []}'; // Uproszczone dla przykładu, możesz zostawić file_get_contents
6192
$responseData = json_decode($jsonResponse, true);
6293
$stream->__toString()->willReturn($jsonResponse);
6394
$response->getBody()->willReturn($stream);
@@ -82,21 +113,27 @@ public function it_should_return_error_response(
82113
$request->getMethod()->willReturn(Request::METHOD_GET);
83114
$uri = 'rest/v5/search/example_channel?query=bag&sid=123&format=json';
84115
$_SERVER['REQUEST_URI'] = sprintf('/fact-finder/proxy/%s', $uri);
116+
85117
$this->client->request(Request::METHOD_GET, $uri)->willThrow(new ConnectException('Unable to connect with server.', $requestInterface->getWrappedObject()));
86118
$eventDispatcher->dispatch(Argument::type(BeforeProxyErrorResponseEvent::class))->willReturn($event);
119+
$event->getResponse()->willReturn(new JsonResponse(['message' => 'Unable to connect with server.'], Response::HTTP_BAD_REQUEST));
87120

88121
// When
89122
$response = $this->execute('rest/v5/search/example_channel', $request, $this->clientBuilder, $eventDispatcher);
90123

91124
// Then
92125
$response->shouldBeAnInstanceOf(JsonResponse::class);
93-
Assert::assertEquals(
94-
['message' => 'Unable to connect with server.'],
95-
json_decode($response->getWrappedObject()->getContent(), true)
96-
);
97-
Assert::assertEquals(
98-
Response::HTTP_BAD_REQUEST,
99-
$response->getWrappedObject()->getStatusCode()
100-
);
126+
Assert::assertEquals(Response::HTTP_BAD_REQUEST, $response->getWrappedObject()->getStatusCode());
127+
}
128+
129+
public function it_should_throw_exception_if_proxy_is_disabled(
130+
Communication $config,
131+
Request $request,
132+
ClientBuilder $clientBuilder,
133+
EventDispatcherInterface $eventDispatcher
134+
): void {
135+
$config->isProxyEnabled()->willReturn(false);
136+
$this->shouldThrow(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class)
137+
->during('execute', ['some-endpoint', $request, $clientBuilder, $eventDispatcher]);
101138
}
102139
}

src/Storefront/Controller/ProxyController.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\HttpFoundation\JsonResponse;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1718
use Symfony\Component\Routing\Annotation\Route;
1819

1920
#[Route(defaults: ['_routeScope' => ['storefront']])]
@@ -39,9 +40,22 @@ public function execute(
3940
ClientBuilder $clientBuilder,
4041
EventDispatcherInterface $eventDispatcher,
4142
): Response {
43+
if (!$this->config->isProxyEnabled()) {
44+
throw new NotFoundHttpException('Proxy is disabled.');
45+
}
46+
47+
$apiKey = $request->headers->get('x-ff-api-key');
48+
49+
if (!$apiKey) {
50+
return new JsonResponse(
51+
['message' => 'UNAUTHORIZED'],
52+
Response::HTTP_UNAUTHORIZED
53+
);
54+
}
55+
4256
$client = $clientBuilder
4357
->withServerUrl($this->config->getServerUrl() . '/')
44-
->withApiKey($this->config->getApiKey())
58+
->withApiKey($apiKey)
4559
->withVersion($this->config->getVersion())
4660
->build();
4761
$query = (string) parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_QUERY);

0 commit comments

Comments
 (0)