From 07122a5f1547ab16b4d6e8d7a7b348622de0b47c 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 87f26c52cd5..7e8dd06393f 100644 --- a/lib/Config.php +++ b/lib/Config.php @@ -25,6 +25,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 665ba5faa0f..ff7b3f183d0 100644 --- a/lib/Controller/RecordingController.php +++ b/lib/Controller/RecordingController.php @@ -28,6 +28,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; @@ -44,6 +45,7 @@ public function __construct( private ParticipantService $participantService, private RecordingService $recordingService, private RoomService $roomService, + private ITimeFactory $timeFactory, private LoggerInterface $logger, ) { parent::__construct($appName, $request); @@ -81,12 +83,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([ @@ -94,6 +98,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 697c98c6d5d..753a4076f2f 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -296,12 +296,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); @@ -326,6 +328,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 44f20b7bd0d..601c520b001 100644 --- a/src/components/AdminSettings/RecordingServer.vue +++ b/src/components/AdminSettings/RecordingServer.vue @@ -174,6 +174,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 0dc8b0832ae..43b6e23f1c6 100644 --- a/src/components/AdminSettings/SignalingServer.vue +++ b/src/components/AdminSettings/SignalingServer.vue @@ -186,6 +186,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', {