From da90e0a7b3c0049b74e787b5a485b72e426a3456 Mon Sep 17 00:00:00 2001 From: Christoph Reiter <reiter.christoph@gmail.com> Date: Wed, 9 Feb 2022 15:28:14 +0100 Subject: [PATCH] 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. --- src/Authenticator/BearerUserProvider.php | 7 +++++++ src/OIDC/OIDProvider.php | 15 ++++++++++++-- src/Service/HealthCheck.php | 25 +++++++++++++++++++----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Authenticator/BearerUserProvider.php b/src/Authenticator/BearerUserProvider.php index 1a7cf75..c0656f1 100644 --- a/src/Authenticator/BearerUserProvider.php +++ b/src/Authenticator/BearerUserProvider.php @@ -31,6 +31,13 @@ class BearerUserProvider implements BearerUserProviderInterface, LoggerAwareInte $this->config = $config; } + public function getValidationLeewaySeconds(): int + { + $config = $this->config; + + return $config['local_validation_leeway']; + } + public function loadUserByToken(string $accessToken): UserInterface { $config = $this->config; diff --git a/src/OIDC/OIDProvider.php b/src/OIDC/OIDProvider.php index 052b1a0..fd961f8 100644 --- a/src/OIDC/OIDProvider.php +++ b/src/OIDC/OIDProvider.php @@ -52,7 +52,7 @@ class OIDProvider implements LoggerAwareInterface $this->clientHandler = $handler; } - private function getClient(): Client + private function getClient(bool $noCache = false): Client { $stack = HandlerStack::create($this->clientHandler); if ($this->logger !== null) { @@ -67,7 +67,7 @@ class OIDProvider implements LoggerAwareInterface $client = new Client($options); - if ($this->cachePool !== null) { + if ($this->cachePool !== null && !$noCache) { $cacheMiddleWare = new CacheMiddleware( new GreedyCacheStrategy( new Psr6CacheStorage($this->cachePool), @@ -80,6 +80,17 @@ class OIDProvider implements LoggerAwareInterface 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 */ diff --git a/src/Service/HealthCheck.php b/src/Service/HealthCheck.php index 29e6c39..2309368 100644 --- a/src/Service/HealthCheck.php +++ b/src/Service/HealthCheck.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Dbp\Relay\AuthBundle\Service; +use Dbp\Relay\AuthBundle\Authenticator\BearerUserProvider; use Dbp\Relay\AuthBundle\OIDC\OIDProvider; use Dbp\Relay\CoreBundle\HealthCheck\CheckInterface; use Dbp\Relay\CoreBundle\HealthCheck\CheckOptions; @@ -11,11 +12,13 @@ use Dbp\Relay\CoreBundle\HealthCheck\CheckResult; 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 @@ -40,12 +43,23 @@ class HealthCheck implements CheckInterface public function checkConfig() { - $this->provider->getProviderConfig(); + $this->oidcProvider->getProviderConfig(); } 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 @@ -53,6 +67,7 @@ class HealthCheck implements CheckInterface $results = []; $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 server time is in sync', [$this, 'checkTimeSync']); return $results; } -- GitLab