Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix up the healthcheck endpoint #2704

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ rebuild:
.PHONY: rebuild

down:
$(COMPOSE) down $(filter-out $@,$(MAKECMDGOALS))
$(COMPOSE) down --remove-orphans $(filter-out $@,$(MAKECMDGOALS))
.PHONY: down

destroy:
Expand Down
30 changes: 30 additions & 0 deletions mock-integrations/image-request-handler/mock-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,36 @@ specFile: "mock-openapi.yaml"
response:
scriptFile: "mock-responses.js"

security:
# no requests permitted by default
default: Deny

# only requests meeting all these conditions are permitted
conditions:
- effect: Permit
requestHeaders:
Authorization:
value: AWS4-HMAC-SHA256 .*
operator: Matches

resources:
# always permit status endpoint
- method: GET
path: /system/status
security:
default: Permit

# always permit _spec viewer endpoint
- method: GET
path: /_spec
security:
default: Permit
- method: GET
path: /_spec/combined.json
security:
default: Permit


pickFirstIfNoneMatch: false
validation:
request: true
Empty file.
35 changes: 35 additions & 0 deletions mock-integrations/lpa-data-store/mock-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,41 @@ specFile: "mock-openapi.yaml"
response:
scriptFile: "mock-responses.js"

security:
# no requests permitted by default
default: Deny

# only requests meeting all these conditions are permitted
conditions:
- effect: Permit
requestHeaders:
Authorization:
value: AWS4-HMAC-SHA256 .*
operator: Matches

- effect: Permit
requestHeaders:
X-Jwt-Authorization:
value: Bearer .*
operator: Matches

resources:
# always permit status endpoint
- method: GET
path: /system/status
security:
default: Permit

# always permit _spec viewer endpoint
- method: GET
path: /_spec
security:
default: Permit
- method: GET
path: /_spec/combined.json
security:
default: Permit

pickFirstIfNoneMatch: false
validation:
request: true
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ public function sign(RequestInterface $request): RequestInterface
$request = $request->withHeader($header, $value);
}

return $this->signer->signRequest($request, $this->credentials);
$request = $this->signer->signRequest($request, $this->credentials);

// if the Authorization header has been supplied then it is with the understanding
// that it replaces anything generated by the AWS signing process.
if (array_key_exists('Authorization', $this->headers)) {
$request = $request->withHeader('Authorization', $this->headers['Authorization']);
}

return $request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

namespace App\Handler\Factory;

use App\DataAccess\ApiGateway\RequestSigner;
use App\DataAccess\ApiGateway\RequestSignerFactory;
use App\DataAccess\Repository\ActorUsersInterface;
use App\Handler\HealthcheckHandler;
use GuzzleHttp\Client as HttpClient;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;

class HealthcheckHandlerFactory
{
Expand All @@ -22,10 +23,11 @@ public function __invoke(ContainerInterface $container): HealthcheckHandler
$config = $container->get('config');

return new HealthcheckHandler(
$config['version'],
$container->get(ClientInterface::class),
$container->get(RequestFactoryInterface::class),
$container->get(RequestSignerFactory::class),
$container->get(ActorUsersInterface::class),
$container->get(HttpClient::class),
$container->get(RequestSigner::class),
$config['version'],
$config['sirius_api']['endpoint'],
$config['codes_api']['endpoint'],
$config['iap_images_api']['endpoint']
Expand Down
77 changes: 52 additions & 25 deletions service-api/app/src/App/src/Handler/HealthcheckHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Handler;

use App\DataAccess\ApiGateway\RequestSigner;
use App\DataAccess\ApiGateway\RequestSignerFactory;
use App\DataAccess\ApiGateway\SignatureType;
use App\DataAccess\Repository\ActorUsersInterface;
use Fig\Http\Message\StatusCodeInterface;
use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\Psr7\Request;
use Laminas\Diactoros\Response\JsonResponse;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Expand All @@ -19,13 +22,14 @@
final class HealthcheckHandler implements RequestHandlerInterface
{
public function __construct(
private string $version,
private ActorUsersInterface $actorUsers,
private HttpClient $httpClient,
private RequestSigner $awsSignature,
private string $siriusApiUrl,
private string $codesApiUrl,
private string $iapImagesApiUrl,
private readonly ClientInterface $httpClient,
private readonly RequestFactoryInterface $requestFactory,
private readonly RequestSignerFactory $requestSignerFactory,
private readonly ActorUsersInterface $actorUsers,
private readonly string $version,
private readonly string $siriusApiUrl,
private readonly string $codesApiUrl,
private readonly string $iapImagesApiUrl,
) {
}

Expand All @@ -52,18 +56,49 @@ private function isHealthy(array $data): bool
&& $data['iap_images_api']['healthy'];
}

/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function checkIapImagesApi(): array
{
$url = sprintf('%s/v1/healthcheck', $this->iapImagesApiUrl);
$request = $this->requestFactory->createRequest(
'GET',
sprintf('%s/v1/healthcheck', $this->iapImagesApiUrl),
);
$request = ($this->requestSignerFactory)()->sign($request);

return $this->apiCall(new Request('GET', $url));
return $this->apiCall($request);
}

/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function checkApiEndpoint(): array
{
$url = sprintf('%s/v1/healthcheck', $this->siriusApiUrl);
$request = $this->requestFactory->createRequest(
'GET',
sprintf('%s/v1/healthcheck', $this->siriusApiUrl),
);
$request = ($this->requestSignerFactory)()->sign($request);

return $this->apiCall(new Request('GET', $url));
return $this->apiCall($request);
}

/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function checkCodesApiEndpoint(): array
{
$request = $this->requestFactory->createRequest(
'GET',
sprintf('%s/v1/healthcheck', $this->codesApiUrl),
);
$request = ($this->requestSignerFactory)(SignatureType::ActorCodes)->sign($request);

return $this->apiCall($request);
}

private function checkDynamoEndpoint(): array
Expand All @@ -86,24 +121,16 @@ private function checkDynamoEndpoint(): array
return $data;
}

private function checkCodesApiEndpoint(): array
{
$url = sprintf('%s/v1/healthcheck', $this->codesApiUrl);

return $this->apiCall(new Request('GET', $url));
}

/**
* @param RequestInterface $request
* @param RequestInterface $uri
Comment on lines -97 to +125
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right? The PHP typehints suggest the parameter is still called $request

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what happened here. Will fix.

* @return array{healthy: bool}
*/
private function apiCall(RequestInterface $request): array
{
$data = [];
$signedRequest = $this->awsSignature->sign($request);
$data = [];

try {
$response = $this->httpClient->send($signedRequest);
$response = $this->httpClient->sendRequest($request);

if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) {
$data['healthy'] = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

namespace AppTest\Handler\Factory;

use App\DataAccess\ApiGateway\RequestSigner;
use App\DataAccess\ApiGateway\RequestSignerFactory;
use App\DataAccess\Repository\ActorUsersInterface;
use App\DataAccess\Repository\LpasInterface;
use App\Handler\Factory\HealthcheckHandlerFactory;
use App\Handler\HealthcheckHandler;
use GuzzleHttp\Client as HttpClient;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Container\ContainerInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;

class HealthcheckHandlerFactoryTest extends TestCase
{
Expand All @@ -22,11 +22,11 @@ public function testItCreatesAHealthcheckHandler(): void
{
$factory = new HealthcheckHandlerFactory();

$actorUsers = $this->prophesize(ActorUsersInterface::class);
$lpaInterface = $this->prophesize(LpasInterface::class);
$container = $this->prophesize(ContainerInterface::class);
$httpClientProphecy = $this->prophesize(HttpClient::class);
$requestSignerProphecy = $this->prophesize(RequestSigner::class);
$container = $this->prophesize(ContainerInterface::class);
$actorUsers = $this->prophesize(ActorUsersInterface::class);
$clientProphecy = $this->prophesize(ClientInterface::class);
$requestFactoryProphecy = $this->prophesize(RequestFactoryInterface::class);
$requestSignerProphecy = $this->prophesize(RequestSignerFactory::class);

$container->get('config')->willReturn(
[
Expand All @@ -44,9 +44,9 @@ public function testItCreatesAHealthcheckHandler(): void
);

$container->get(ActorUsersInterface::class)->willReturn($actorUsers->reveal());
$container->get(LpasInterface::class)->willReturn($lpaInterface->reveal());
$container->get(HttpClient::class)->willReturn($httpClientProphecy->reveal());
$container->get(RequestSigner::class)->willReturn($requestSignerProphecy->reveal());
$container->get(ClientInterface::class)->willReturn($clientProphecy->reveal());
$container->get(RequestFactoryInterface::class)->willReturn($requestFactoryProphecy->reveal());
$container->get(RequestSignerFactory::class)->willReturn($requestSignerProphecy->reveal());

$handler = $factory($container->reveal());

Expand Down
Loading
Loading