From 9290632bba85bc75a22677d6d78cc83ea39f7824 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 a5f7a720802..cb9b766ff2c 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 76d5ef91ae7..0c4199f9b98 100644 --- a/lib/Controller/RecordingController.php +++ b/lib/Controller/RecordingController.php @@ -46,6 +46,7 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired; 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); @@ -98,12 +100,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([ @@ -111,6 +115,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 5ca579ae137..24cb76f01ac 100644 --- a/lib/Controller/SignalingController.php +++ b/lib/Controller/SignalingController.php @@ -258,12 +258,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); @@ -288,6 +290,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 7fa4fab614a..2aa8a6b6271 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 fc4783bc355..ce8cd23a734 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', {