From b769c0de08e6385b2111d434b4ce89bdb5b0ac08 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 18 Dec 2024 06:59:29 +0100 Subject: [PATCH] fix(backends): Check times of the backend servers Signed-off-by: Joas Schilling --- lib/Config.php | 1 + lib/Controller/RecordingController.php | 12 ++++++++++++ lib/Controller/SignalingController.php | 10 ++++++++++ src/components/AdminSettings/RecordingServer.vue | 2 ++ src/components/AdminSettings/SignalingServer.vue | 2 ++ 5 files changed, 27 insertions(+) diff --git a/lib/Config.php b/lib/Config.php index 0228c35733d..c72a0252de9 100644 --- a/lib/Config.php +++ b/lib/Config.php @@ -38,6 +38,7 @@ use OCP\Security\ISecureRandom; class Config { + public const ALLOWED_BACKEND_TIMEOFFSET = 45; public const SIGNALING_INTERNAL = 'internal'; public const SIGNALING_EXTERNAL = 'external'; public const SIGNALING_CLUSTER_CONVERSATION = 'conversation_cluster'; diff --git a/lib/Controller/RecordingController.php b/lib/Controller/RecordingController.php index 6a5c11f0c4c..e2304559e63 100644 --- a/lib/Controller/RecordingController.php +++ b/lib/Controller/RecordingController.php @@ -46,6 +46,7 @@ use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Http\Client\IClientService; use OCP\IRequest; use Psr\Log\LoggerInterface; @@ -62,6 +63,7 @@ public function __construct( private ParticipantService $participantService, private RecordingService $recordingService, private RoomService $roomService, + private ITimeFactory $timeFactory, private LoggerInterface $logger, ) { parent::__construct($appName, $request); @@ -99,12 +101,14 @@ public function getWelcomeMessage(int $serverId): DataResponse { $client = $this->clientService->newClient(); try { + $timeBefore = $this->timeFactory->getTime(); $response = $client->get($url . '/api/v1/welcome', [ 'verify' => $verifyServer, 'nextcloud' => [ 'allow_local_address' => true, ], ]); + $timeAfter = $this->timeFactory->getTime(); if ($response->getHeader(\OCA\Talk\Signaling\Manager::FEATURE_HEADER)) { return new DataResponse([ @@ -112,6 +116,14 @@ public function getWelcomeMessage(int $serverId): DataResponse { ], Http::STATUS_INTERNAL_SERVER_ERROR); } + $responseTime = $this->timeFactory->getDateTime($response->getHeader('date'))->getTimestamp(); + if (($timeBefore - Config::ALLOWED_BACKEND_TIMEOFFSET) > $responseTime + || ($timeAfter + Config::ALLOWED_BACKEND_TIMEOFFSET) < $responseTime) { + return new DataResponse([ + 'error' => 'TIME_OUT_OF_SYNC', + ], Http::STATUS_INTERNAL_SERVER_ERROR); + } + $body = $response->getBody(); $data = json_decode($body, true); diff --git a/lib/Controller/SignalingController.php b/lib/Controller/SignalingController.php index 90d53d086c8..f4bfa1607d6 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -256,12 +256,14 @@ public function getWelcomeMessage(int $serverId): DataResponse { $client = $this->clientService->newClient(); try { + $timeBefore = $this->timeFactory->getTime(); $response = $client->get($url . '/api/v1/welcome', [ 'verify' => $verifyServer, 'nextcloud' => [ 'allow_local_address' => true, ], ]); + $timeAfter = $this->timeFactory->getTime(); $body = $response->getBody(); $data = json_decode($body, true); @@ -286,6 +288,14 @@ public function getWelcomeMessage(int $serverId): DataResponse { ], Http::STATUS_INTERNAL_SERVER_ERROR); } + $responseTime = $this->timeFactory->getDateTime($response->getHeader('date'))->getTimestamp(); + if (($timeBefore - Config::ALLOWED_BACKEND_TIMEOFFSET) > $responseTime + || ($timeAfter + Config::ALLOWED_BACKEND_TIMEOFFSET) < $responseTime) { + return new DataResponse([ + 'error' => 'TIME_OUT_OF_SYNC', + ], Http::STATUS_INTERNAL_SERVER_ERROR); + } + $missingFeatures = $this->signalingManager->getSignalingServerMissingFeatures($response); if (!empty($missingFeatures)) { return new DataResponse([ diff --git a/src/components/AdminSettings/RecordingServer.vue b/src/components/AdminSettings/RecordingServer.vue index 61a45dd0b46..427b9f85f61 100644 --- a/src/components/AdminSettings/RecordingServer.vue +++ b/src/components/AdminSettings/RecordingServer.vue @@ -175,6 +175,8 @@ export default { this.errorMessage = t('spreed', 'Error: Server did not respond with proper JSON') } else if (error === 'CERTIFICATE_EXPIRED') { this.errorMessage = t('spreed', 'Error: Certificate expired') + } else if (error === 'TIME_OUT_OF_SYNC') { + this.errorMessage = t('spreed', 'Error: System times of Nextcloud server and Recording backend server are out of sync. Please make sure that both servers are connected to a time-server or manually synchronize their time.') } else if (error) { this.errorMessage = t('spreed', 'Error: Server responded with: {error}', data) } else { diff --git a/src/components/AdminSettings/SignalingServer.vue b/src/components/AdminSettings/SignalingServer.vue index d239e47b42b..2f647a8674f 100644 --- a/src/components/AdminSettings/SignalingServer.vue +++ b/src/components/AdminSettings/SignalingServer.vue @@ -187,6 +187,8 @@ export default { this.errorMessage = t('spreed', 'Error: Server did not respond with proper JSON') } else if (error === 'CERTIFICATE_EXPIRED') { this.errorMessage = t('spreed', 'Error: Certificate expired') + } else if (error === 'TIME_OUT_OF_SYNC') { + this.errorMessage = t('spreed', 'Error: System times of Nextcloud server and High-performance backend server are out of sync. Please make sure that both servers are connected to a time-server or manually synchronize their time.') } else if (error === 'UPDATE_REQUIRED') { this.versionFound = data.version || t('spreed', 'Could not get version') this.errorMessage = t('spreed', 'Error: Running version: {version}; Server needs to be updated to be compatible with this version of Talk', {