From 11bbb70d5470a3ac95ff9a73de1ef9e29f168304 Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 26 Aug 2024 14:14:04 +0200 Subject: [PATCH 1/4] chore: add minimum client version validation in websocket connection establishment --- services/appflowy-collaborate/src/api.rs | 14 +++++++++----- services/appflowy-collaborate/src/config.rs | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/services/appflowy-collaborate/src/api.rs b/services/appflowy-collaborate/src/api.rs index 45cd94bf..e677b406 100644 --- a/services/appflowy-collaborate/src/api.rs +++ b/services/appflowy-collaborate/src/api.rs @@ -3,9 +3,9 @@ use std::str::FromStr; use std::time::Duration; use actix::Addr; -use actix_http::header::{HeaderMap, AUTHORIZATION}; +use actix_http::header::{AUTHORIZATION, HeaderMap}; +use actix_web::{HttpRequest, HttpResponse, Result, Scope, web}; use actix_web::web::{Data, Json, Payload, PayloadConfig}; -use actix_web::{web, HttpRequest, HttpResponse, Result, Scope}; use actix_web_actors::ws; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; @@ -18,8 +18,9 @@ use tokio_stream::StreamExt; use tracing::{debug, error, event, instrument, trace}; use app_error::AppError; -use collab_rt_entity::user::{AFUserChange, RealtimeUser, UserMessage}; +use authentication::jwt::{authorization_from_token, UserUuid}; use collab_rt_entity::{HttpRealtimeMessage, RealtimeMessage}; +use collab_rt_entity::user::{AFUserChange, RealtimeUser, UserMessage}; use shared_entity::response::{AppResponse, AppResponseError}; use crate::actix_ws::client::RealtimeClient; @@ -28,10 +29,9 @@ use crate::actix_ws::server::RealtimeServerActor; use crate::collab::access_control::RealtimeCollabAccessControlImpl; use crate::collab::storage::CollabAccessControlStorage; use crate::compression::{ - decompress, CompressionType, X_COMPRESSION_BUFFER_SIZE, X_COMPRESSION_TYPE, + CompressionType, decompress, X_COMPRESSION_BUFFER_SIZE, X_COMPRESSION_TYPE, }; use crate::state::AppState; -use authentication::jwt::{authorization_from_token, UserUuid}; pub fn ws_scope() -> Scope { web::scope("/ws").service(web::resource("/v1").route(web::get().to(establish_ws_connection_v1))) @@ -76,6 +76,10 @@ pub async fn establish_ws_connection_v1( }, }; + if client_version < state.config.websocket.min_client_version { + return Err(AppError::Connect("Client version is too low".to_string()).into()); + } + start_connect( &request, payload, diff --git a/services/appflowy-collaborate/src/config.rs b/services/appflowy-collaborate/src/config.rs index fe807f62..a595f887 100644 --- a/services/appflowy-collaborate/src/config.rs +++ b/services/appflowy-collaborate/src/config.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use anyhow::Context; use secrecy::Secret; +use semver::Version; use serde::Deserialize; use sqlx::postgres::{PgConnectOptions, PgSslMode}; @@ -70,6 +71,7 @@ impl AISettings { pub struct WebsocketSetting { pub heartbeat_interval: u8, pub client_timeout: u8, + pub min_client_version: Version, } #[derive(Clone, Debug)] @@ -140,6 +142,7 @@ pub fn get_configuration() -> Result { websocket: WebsocketSetting { heartbeat_interval: get_env_var("APPFLOWY_WEBSOCKET_HEARTBEAT_INTERVAL", "6").parse()?, client_timeout: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_TIMEOUT", "60").parse()?, + min_client_version: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_MIN_VERSION", "0.5.0").parse()?, }, db_settings: DatabaseSetting { pg_conn_opts: PgConnectOptions::from_str(&get_env_var( From 8d1d563076d8439624f2a0d0bbdde2c0b78bb3d8 Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 26 Aug 2024 14:20:39 +0200 Subject: [PATCH 2/4] chore: fix formatting --- services/appflowy-collaborate/src/api.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/appflowy-collaborate/src/api.rs b/services/appflowy-collaborate/src/api.rs index e677b406..36b6ba5c 100644 --- a/services/appflowy-collaborate/src/api.rs +++ b/services/appflowy-collaborate/src/api.rs @@ -3,9 +3,9 @@ use std::str::FromStr; use std::time::Duration; use actix::Addr; -use actix_http::header::{AUTHORIZATION, HeaderMap}; -use actix_web::{HttpRequest, HttpResponse, Result, Scope, web}; +use actix_http::header::{HeaderMap, AUTHORIZATION}; use actix_web::web::{Data, Json, Payload, PayloadConfig}; +use actix_web::{web, HttpRequest, HttpResponse, Result, Scope}; use actix_web_actors::ws; use anyhow::anyhow; use bytes::{Bytes, BytesMut}; @@ -19,8 +19,8 @@ use tracing::{debug, error, event, instrument, trace}; use app_error::AppError; use authentication::jwt::{authorization_from_token, UserUuid}; -use collab_rt_entity::{HttpRealtimeMessage, RealtimeMessage}; use collab_rt_entity::user::{AFUserChange, RealtimeUser, UserMessage}; +use collab_rt_entity::{HttpRealtimeMessage, RealtimeMessage}; use shared_entity::response::{AppResponse, AppResponseError}; use crate::actix_ws::client::RealtimeClient; @@ -29,7 +29,7 @@ use crate::actix_ws::server::RealtimeServerActor; use crate::collab::access_control::RealtimeCollabAccessControlImpl; use crate::collab::storage::CollabAccessControlStorage; use crate::compression::{ - CompressionType, decompress, X_COMPRESSION_BUFFER_SIZE, X_COMPRESSION_TYPE, + decompress, CompressionType, X_COMPRESSION_BUFFER_SIZE, X_COMPRESSION_TYPE, }; use crate::state::AppState; From 43f6f3bc61773b97e736c918bbcbb618d8e3a69d Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 26 Aug 2024 15:03:18 +0200 Subject: [PATCH 3/4] chore: apply same change to other ws endpoint --- src/api/ws.rs | 4 ++++ src/config/config.rs | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/api/ws.rs b/src/api/ws.rs index 3372d7aa..11249588 100644 --- a/src/api/ws.rs +++ b/src/api/ws.rs @@ -86,6 +86,10 @@ pub async fn establish_ws_connection_v1( }, }; + if client_version < state.config.websocket.min_client_version { + return Err(AppError::Connect("Client version is too low".to_string()).into()); + } + start_connect( &request, payload, diff --git a/src/config/config.rs b/src/config/config.rs index c0ad3fc1..349c8ec1 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -1,11 +1,14 @@ -use anyhow::Context; -use infra::env_util::get_env_var; -use secrecy::{ExposeSecret, Secret}; -use serde::Deserialize; -use sqlx::postgres::{PgConnectOptions, PgSslMode}; use std::fmt::Display; use std::str::FromStr; +use anyhow::Context; +use secrecy::{ExposeSecret, Secret}; +use semver::Version; +use serde::Deserialize; +use sqlx::postgres::{PgConnectOptions, PgSslMode}; + +use infra::env_util::get_env_var; + #[derive(Clone, Debug)] pub struct Config { pub app_env: Environment, @@ -166,6 +169,7 @@ pub fn get_configuration() -> Result { websocket: WebsocketSetting { heartbeat_interval: get_env_var("APPFLOWY_WEBSOCKET_HEARTBEAT_INTERVAL", "6").parse()?, client_timeout: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_TIMEOUT", "60").parse()?, + min_client_version: get_env_var("APPFLOWY_WEBSOCKET_CLIENT_MIN_VERSION", "0.5.0").parse()?, }, redis_uri: get_env_var("APPFLOWY_REDIS_URI", "redis://localhost:6379").into(), s3: S3Setting { @@ -239,4 +243,5 @@ impl FromStr for Environment { pub struct WebsocketSetting { pub heartbeat_interval: u8, pub client_timeout: u8, + pub min_client_version: Version, } From 69f7b7f6ddeb45f67123f6b5eb12b24072162b17 Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 26 Aug 2024 18:52:08 +0200 Subject: [PATCH 4/4] chore: drop endpoints used before client version 0.5.0 --- src/api/ws.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/ws.rs b/src/api/ws.rs index 11249588..d23b4e40 100644 --- a/src/api/ws.rs +++ b/src/api/ws.rs @@ -26,7 +26,7 @@ use crate::state::AppState; pub fn ws_scope() -> Scope { web::scope("/ws") - .service(establish_ws_connection) + //.service(establish_ws_connection) .service(web::resource("/v1").route(web::get().to(establish_ws_connection_v1))) } const MAX_FRAME_SIZE: usize = 65_536; // 64 KiB