Skip to content
Snippets Groups Projects
Commit da90e0a7 authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Add a health check for the server time sync

We depend on the api server and the keycloak server being in sync
so we can decide if the tokens are valid.

Make sure the system times are within the configured leeway.
parent 6768c03b
No related branches found
No related tags found
No related merge requests found
Pipeline #86920 passed
...@@ -31,6 +31,13 @@ class BearerUserProvider implements BearerUserProviderInterface, LoggerAwareInte ...@@ -31,6 +31,13 @@ class BearerUserProvider implements BearerUserProviderInterface, LoggerAwareInte
$this->config = $config; $this->config = $config;
} }
public function getValidationLeewaySeconds(): int
{
$config = $this->config;
return $config['local_validation_leeway'];
}
public function loadUserByToken(string $accessToken): UserInterface public function loadUserByToken(string $accessToken): UserInterface
{ {
$config = $this->config; $config = $this->config;
......
...@@ -52,7 +52,7 @@ class OIDProvider implements LoggerAwareInterface ...@@ -52,7 +52,7 @@ class OIDProvider implements LoggerAwareInterface
$this->clientHandler = $handler; $this->clientHandler = $handler;
} }
private function getClient(): Client private function getClient(bool $noCache = false): Client
{ {
$stack = HandlerStack::create($this->clientHandler); $stack = HandlerStack::create($this->clientHandler);
if ($this->logger !== null) { if ($this->logger !== null) {
...@@ -67,7 +67,7 @@ class OIDProvider implements LoggerAwareInterface ...@@ -67,7 +67,7 @@ class OIDProvider implements LoggerAwareInterface
$client = new Client($options); $client = new Client($options);
if ($this->cachePool !== null) { if ($this->cachePool !== null && !$noCache) {
$cacheMiddleWare = new CacheMiddleware( $cacheMiddleWare = new CacheMiddleware(
new GreedyCacheStrategy( new GreedyCacheStrategy(
new Psr6CacheStorage($this->cachePool), new Psr6CacheStorage($this->cachePool),
...@@ -80,6 +80,17 @@ class OIDProvider implements LoggerAwareInterface ...@@ -80,6 +80,17 @@ class OIDProvider implements LoggerAwareInterface
return $client; return $client;
} }
public function getProviderDateTime(): \DateTimeInterface
{
$serverUrl = $this->config['server_url'] ?? '';
$client = $this->getClient(true);
$response = $client->request('GET', $serverUrl);
$date = new \DateTimeImmutable($response->getHeader('Date')[0]);
$date = $date->setTimezone(new \DateTimeZone('UTC'));
return $date;
}
/** /**
* @throws OIDError * @throws OIDError
*/ */
......
...@@ -4,6 +4,7 @@ declare(strict_types=1); ...@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Dbp\Relay\AuthBundle\Service; namespace Dbp\Relay\AuthBundle\Service;
use Dbp\Relay\AuthBundle\Authenticator\BearerUserProvider;
use Dbp\Relay\AuthBundle\OIDC\OIDProvider; use Dbp\Relay\AuthBundle\OIDC\OIDProvider;
use Dbp\Relay\CoreBundle\HealthCheck\CheckInterface; use Dbp\Relay\CoreBundle\HealthCheck\CheckInterface;
use Dbp\Relay\CoreBundle\HealthCheck\CheckOptions; use Dbp\Relay\CoreBundle\HealthCheck\CheckOptions;
...@@ -11,11 +12,13 @@ use Dbp\Relay\CoreBundle\HealthCheck\CheckResult; ...@@ -11,11 +12,13 @@ use Dbp\Relay\CoreBundle\HealthCheck\CheckResult;
class HealthCheck implements CheckInterface class HealthCheck implements CheckInterface
{ {
private $provider; private $oidcProvider;
private $userProvider;
public function __construct(OIDProvider $provider) public function __construct(OIDProvider $oidcProvider, BearerUserProvider $userProvider)
{ {
$this->provider = $provider; $this->oidcProvider = $oidcProvider;
$this->userProvider = $userProvider;
} }
public function getName(): string public function getName(): string
...@@ -40,12 +43,23 @@ class HealthCheck implements CheckInterface ...@@ -40,12 +43,23 @@ class HealthCheck implements CheckInterface
public function checkConfig() public function checkConfig()
{ {
$this->provider->getProviderConfig(); $this->oidcProvider->getProviderConfig();
} }
public function checkPublicKey() public function checkPublicKey()
{ {
$this->provider->getJWKs(); $this->oidcProvider->getJWKs();
}
public function checkTimeSync()
{
$providerTime = $this->oidcProvider->getProviderDateTime();
$systemTime = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
$leeway = $this->userProvider->getValidationLeewaySeconds();
$difference = abs($providerTime->getTimestamp() - $systemTime->getTimestamp());
if ($difference > $leeway) {
throw new \RuntimeException("The system time and the OIDC server time is out of sync ($difference > $leeway seconds)");
}
} }
public function check(CheckOptions $options): array public function check(CheckOptions $options): array
...@@ -53,6 +67,7 @@ class HealthCheck implements CheckInterface ...@@ -53,6 +67,7 @@ class HealthCheck implements CheckInterface
$results = []; $results = [];
$results[] = $this->checkMethod('Check if the OIDC config can be fetched', [$this, 'checkConfig']); $results[] = $this->checkMethod('Check if the OIDC config can be fetched', [$this, 'checkConfig']);
$results[] = $this->checkMethod('Check if the OIDC public key can be fetched', [$this, 'checkPublicKey']); $results[] = $this->checkMethod('Check if the OIDC public key can be fetched', [$this, 'checkPublicKey']);
$results[] = $this->checkMethod('Check if the OIDC server time is in sync', [$this, 'checkTimeSync']);
return $results; return $results;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment