fix: only encrypt if enable (#3236)

* fix: error page display issue

* fix: override document with empty data

* chore: add logs

* fix: encrypt errors

* fix: encrypt errors
This commit is contained in:
Nathan.fooo 2023-08-18 22:32:51 +08:00 committed by GitHub
parent 23a34af30f
commit de01bf70cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 171 additions and 106 deletions

View File

@ -107,6 +107,9 @@ class SupabaseAuthService implements AuthService {
if (!isSupabaseEnabled) { if (!isSupabaseEnabled) {
return _appFlowyAuthService.signUpWithOAuth(platform: platform); return _appFlowyAuthService.signUpWithOAuth(platform: platform);
} }
// Before signing in, sign out any existing users. Otherwise, the callback will be triggered even if the user doesn't click the 'Sign In' button on the website
await _auth.signOut();
final provider = platform.toProvider(); final provider = platform.toProvider();
final completer = supabaseLoginCompleter( final completer = supabaseLoginCompleter(
onSuccess: (userId, userEmail) async { onSuccess: (userId, userEmail) async {

View File

@ -1,4 +1,3 @@
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_svg/flowy_svg.dart'; import 'package:flowy_svg/flowy_svg.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -71,6 +70,8 @@ class FlowyErrorPage extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const FlowyText.medium( const FlowyText.medium(
"AppFlowy Error", "AppFlowy Error",
@ -81,12 +82,14 @@ class FlowyErrorPage extends StatelessWidget {
), ),
FlowyText.semibold( FlowyText.semibold(
message, message,
maxLines: 10,
), ),
const SizedBox( const SizedBox(
height: _titleToMessagePadding, height: _titleToMessagePadding,
), ),
FlowyText.regular( FlowyText.regular(
howToFix, howToFix,
maxLines: 10,
), ),
const SizedBox( const SizedBox(
height: _titleToMessagePadding, height: _titleToMessagePadding,

View File

@ -24,7 +24,7 @@ use flowy_user::event_map::UserCloudServiceProvider;
use flowy_user::services::database::{ use flowy_user::services::database::{
get_user_profile, get_user_workspace, open_collab_db, open_user_db, get_user_profile, get_user_workspace, open_collab_db, open_user_db,
}; };
use flowy_user_deps::cloud::{UserCloudConfig, UserService}; use flowy_user_deps::cloud::UserService;
use flowy_user_deps::entities::*; use flowy_user_deps::entities::*;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
@ -75,25 +75,15 @@ impl AppFlowyServerProvider {
pub fn new( pub fn new(
config: AppFlowyCoreConfig, config: AppFlowyCoreConfig,
provider_type: ServerProviderType, provider_type: ServerProviderType,
cloud_config: Option<UserCloudConfig>,
store_preferences: Weak<StorePreferences>, store_preferences: Weak<StorePreferences>,
) -> Self { ) -> Self {
let enable_sync = cloud_config let encryption = EncryptionImpl::new(None);
.as_ref()
.map(|config| config.enable_sync)
.unwrap_or(true);
let encryption = EncryptionImpl::new(
cloud_config
.as_ref()
.map(|config| config.encrypt_secret.clone()),
);
Self { Self {
config, config,
provider_type: RwLock::new(provider_type), provider_type: RwLock::new(provider_type),
device_id: Default::default(), device_id: Default::default(),
providers: RwLock::new(HashMap::new()), providers: RwLock::new(HashMap::new()),
enable_sync: RwLock::new(enable_sync), enable_sync: RwLock::new(true),
encryption: RwLock::new(Arc::new(encryption)), encryption: RwLock::new(Arc::new(encryption)),
store_preferences, store_preferences,
} }
@ -177,6 +167,7 @@ impl UserCloudServiceProvider for AppFlowyServerProvider {
} }
fn set_encrypt_secret(&self, secret: String) { fn set_encrypt_secret(&self, secret: String) {
tracing::info!("🔑Set encrypt secret");
self.encryption.write().set_secret(secret); self.encryption.write().set_secret(secret);
} }

View File

@ -22,7 +22,7 @@ use flowy_sqlite::kv::StorePreferences;
use flowy_task::{TaskDispatcher, TaskRunner}; use flowy_task::{TaskDispatcher, TaskRunner};
use flowy_user::event_map::{SignUpContext, UserCloudServiceProvider, UserStatusCallback}; use flowy_user::event_map::{SignUpContext, UserCloudServiceProvider, UserStatusCallback};
use flowy_user::manager::{UserManager, UserSessionConfig}; use flowy_user::manager::{UserManager, UserSessionConfig};
use flowy_user::services::cloud_config::get_cloud_config; use flowy_user_deps::cloud::UserCloudConfig;
use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace}; use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace};
use lib_dispatch::prelude::*; use lib_dispatch::prelude::*;
use lib_dispatch::runtime::tokio_default_runtime; use lib_dispatch::runtime::tokio_default_runtime;
@ -150,7 +150,6 @@ impl AppFlowyCore {
let server_provider = Arc::new(AppFlowyServerProvider::new( let server_provider = Arc::new(AppFlowyServerProvider::new(
config.clone(), config.clone(),
provider_type, provider_type,
get_cloud_config(&store_preference),
Arc::downgrade(&store_preference), Arc::downgrade(&store_preference),
)); ));
@ -293,6 +292,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
fn did_init( fn did_init(
&self, &self,
user_id: i64, user_id: i64,
cloud_config: &Option<UserCloudConfig>,
user_workspace: &UserWorkspace, user_workspace: &UserWorkspace,
_device_id: &str, _device_id: &str,
) -> Fut<FlowyResult<()>> { ) -> Fut<FlowyResult<()>> {
@ -303,6 +303,17 @@ impl UserStatusCallback for UserStatusCallbackImpl {
let database_manager = self.database_manager.clone(); let database_manager = self.database_manager.clone();
let document_manager = self.document_manager.clone(); let document_manager = self.document_manager.clone();
if let Some(cloud_config) = cloud_config {
self
.server_provider
.set_enable_sync(cloud_config.enable_sync);
if cloud_config.enable_encrypt() {
self
.server_provider
.set_encrypt_secret(cloud_config.encrypt_secret.clone());
}
}
to_fut(async move { to_fut(async move {
collab_builder.initialize(user_workspace.id.clone()); collab_builder.initialize(user_workspace.id.clone());
folder_manager folder_manager

View File

@ -175,7 +175,7 @@ impl DatabaseManager {
let database = wdb let database = wdb
.get_database(database_id) .get_database(database_id)
.await .await
.ok_or_else(FlowyError::record_not_found)?; .ok_or_else(FlowyError::collab_not_sync)?;
let editor = Arc::new(DatabaseEditor::new(database, self.task_scheduler.clone()).await?); let editor = Arc::new(DatabaseEditor::new(database, self.task_scheduler.clone()).await?);
editors.insert(database_id.to_string(), editor.clone()); editors.insert(database_id.to_string(), editor.clone());

View File

@ -11,7 +11,7 @@ use collab_document::YrsDocAction;
use parking_lot::RwLock; use parking_lot::RwLock;
use flowy_document_deps::cloud::DocumentCloudService; use flowy_document_deps::cloud::DocumentCloudService;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_error::{internal_error, FlowyError, FlowyResult};
use crate::document::MutexDocument; use crate::document::MutexDocument;
use crate::entities::DocumentSnapshotPB; use crate::entities::DocumentSnapshotPB;
@ -71,6 +71,7 @@ impl DocumentManager {
} }
/// Return the document /// Return the document
#[tracing::instrument(level = "debug", skip_all)]
pub async fn get_document(&self, doc_id: &str) -> FlowyResult<Arc<MutexDocument>> { pub async fn get_document(&self, doc_id: &str) -> FlowyResult<Arc<MutexDocument>> {
if let Some(doc) = self.documents.read().get(doc_id) { if let Some(doc) = self.documents.read().get(doc_id) {
return Ok(doc.clone()); return Ok(doc.clone());
@ -78,16 +79,9 @@ impl DocumentManager {
let mut updates = vec![]; let mut updates = vec![];
if !self.is_doc_exist(doc_id)? { if !self.is_doc_exist(doc_id)? {
// Try to get the document from the cloud service // Try to get the document from the cloud service
match self.cloud_service.get_document_updates(doc_id).await { updates = self.cloud_service.get_document_updates(doc_id).await?;
Ok(document_updates) => updates = document_updates,
Err(e) => {
tracing::error!("Get document data failed: {:?}", e);
return Err(FlowyError::internal().context("Can't not read the document data"));
},
}
} }
tracing::debug!("open_document: {:?}", doc_id);
let uid = self.user.user_id()?; let uid = self.user.user_id()?;
let db = self.user.collab_db(uid)?; let db = self.user.collab_db(uid)?;
let collab = self let collab = self
@ -108,17 +102,9 @@ impl DocumentManager {
let mut updates = vec![]; let mut updates = vec![];
if !self.is_doc_exist(doc_id)? { if !self.is_doc_exist(doc_id)? {
if let Ok(document_updates) = self.cloud_service.get_document_updates(doc_id).await { if let Ok(document_updates) = self.cloud_service.get_document_updates(doc_id).await {
if document_updates.is_empty() {
return Err(FlowyError::new(
ErrorCode::UnexpectedEmptyCollabUpdates,
"Can't not read the document data",
));
}
updates = document_updates; updates = document_updates;
} else { } else {
return Err( return Err(FlowyError::collab_not_sync());
FlowyError::record_not_found().context(format!("document: {} is not exist", doc_id)),
);
} }
} }
let uid = self.user.user_id()?; let uid = self.user.user_id()?;

View File

@ -224,8 +224,8 @@ pub enum ErrorCode {
#[error("Invalid decryption secret")] #[error("Invalid decryption secret")]
InvalidEncryptSecret = 74, InvalidEncryptSecret = 74,
#[error("Unexpected empty collab updates")] #[error("It appears that the collaboration object's data has not been fully synchronized")]
UnexpectedEmptyCollabUpdates = 75, CollabDataNotSync = 75,
} }
impl ErrorCode { impl ErrorCode {

View File

@ -88,6 +88,7 @@ impl FlowyError {
unexpect_calendar_field_type, unexpect_calendar_field_type,
ErrorCode::UnexpectedCalendarFieldType ErrorCode::UnexpectedCalendarFieldType
); );
static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync);
} }
impl std::convert::From<ErrorCode> for FlowyError { impl std::convert::From<ErrorCode> for FlowyError {

View File

@ -158,8 +158,8 @@ impl FolderManager {
FolderInitializeData::Raw(raw_data) => { FolderInitializeData::Raw(raw_data) => {
if raw_data.is_empty() { if raw_data.is_empty() {
return Err(FlowyError::new( return Err(FlowyError::new(
ErrorCode::UnexpectedEmptyCollabUpdates, ErrorCode::CollabDataNotSync,
"Can't fetch the workspace data from server", "Can't fetch the workspace from server",
)); ));
} }
let collab = self.collab_for_folder(uid, &workspace_id, collab_db, raw_data)?; let collab = self.collab_for_folder(uid, &workspace_id, collab_db, raw_data)?;

View File

@ -38,9 +38,10 @@ where
tx.send( tx.send(
async move { async move {
let postgrest = try_get_postgrest?; let postgrest = try_get_postgrest?;
FetchObjectUpdateAction::new(object_id.to_string(), object_ty, postgrest) let updates = FetchObjectUpdateAction::new(object_id.to_string(), object_ty, postgrest)
.run_with_fix_interval(5, 10) .run_with_fix_interval(5, 10)
.await .await?;
Ok(updates)
} }
.await, .await,
) )

View File

@ -6,6 +6,7 @@ use collab_plugins::cloud_storage::CollabType;
use tokio::sync::oneshot::channel; use tokio::sync::oneshot::channel;
use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot}; use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
use flowy_error::FlowyError;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use crate::supabase::api::request::{get_snapshots_from_server, FetchObjectUpdateAction}; use crate::supabase::api::request::{get_snapshots_from_server, FetchObjectUpdateAction};
@ -34,7 +35,11 @@ where
async move { async move {
let postgrest = try_get_postgrest?; let postgrest = try_get_postgrest?;
let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest); let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest);
action.run_with_fix_interval(5, 10).await let updates = action.run_with_fix_interval(5, 10).await?;
if updates.is_empty() {
return Err(FlowyError::collab_not_sync().into());
}
Ok(updates)
} }
.await, .await,
) )

View File

@ -65,9 +65,12 @@ impl Action for FetchObjectUpdateAction {
Box::pin(async move { Box::pin(async move {
match weak_postgres.upgrade() { match weak_postgres.upgrade() {
None => Ok(vec![]), None => Ok(vec![]),
Some(postgrest) => { Some(postgrest) => match get_updates_from_server(&object_id, &object_ty, postgrest).await {
let items = get_updates_from_server(&object_id, &object_ty, postgrest).await?; Ok(items) => Ok(items.into_iter().map(|item| item.value).collect()),
Ok(items.into_iter().map(|item| item.value).collect()) Err(err) => {
tracing::error!("Get {} updates failed with error: {:?}", object_id, err);
Err(err)
},
}, },
} }
}) })
@ -112,7 +115,19 @@ impl Action for BatchFetchObjectUpdateAction {
Box::pin(async move { Box::pin(async move {
match weak_postgrest.upgrade() { match weak_postgrest.upgrade() {
None => Ok(CollabObjectUpdateByOid::default()), None => Ok(CollabObjectUpdateByOid::default()),
Some(server) => batch_get_updates_from_server(object_ids, &object_ty, server).await, Some(server) => {
match batch_get_updates_from_server(object_ids.clone(), &object_ty, server).await {
Ok(updates_by_oid) => Ok(updates_by_oid),
Err(err) => {
tracing::error!(
"Batch get object with given ids:{:?} failed with error: {:?}",
object_ids,
err
);
Err(err)
},
}
},
} }
}) })
} }
@ -349,7 +364,13 @@ fn parser_update_from_json(
json.get("value").and_then(|value| value.as_str()), json.get("value").and_then(|value| value.as_str()),
) { ) {
(Some(encrypt), Some(value)) => { (Some(encrypt), Some(value)) => {
SupabaseBinaryColumnDecoder::decode(value, encrypt as i32, encryption_secret).ok() match SupabaseBinaryColumnDecoder::decode(value, encrypt as i32, encryption_secret) {
Ok(value) => Some(value),
Err(err) => {
tracing::error!("Decode value column failed: {:?}", err);
None
},
}
}, },
_ => None, _ => None,
}; };
@ -371,9 +392,12 @@ fn parser_update_from_json(
} }
Ok(UpdateItem { key, value }) Ok(UpdateItem { key, value })
} else { } else {
let keys = json
.as_object()
.map(|map| map.iter().map(|(key, _)| key).collect::<Vec<&String>>());
Err(anyhow::anyhow!( Err(anyhow::anyhow!(
"missing key or value column in json: {:?}", "missing key or value column. Current keys:: {:?}",
json keys
)) ))
} }
} }

View File

@ -152,17 +152,24 @@ impl AppFlowyServer for SupabaseServer {
fn handle_realtime_event(&self, json: Value) { fn handle_realtime_event(&self, json: Value) {
match serde_json::from_value::<RealtimeCollabUpdateEvent>(json) { match serde_json::from_value::<RealtimeCollabUpdateEvent>(json) {
Ok(event) => { Ok(event) => {
if let (Some(tx), Some(secret)) = ( if let Some(tx) = self.update_tx.read().get(event.payload.oid.as_str()) {
self.update_tx.read().get(event.payload.oid.as_str()), tracing::trace!(
self "current device: {}, event device: {}",
.encryption self.did.lock().as_str(),
.upgrade() event.payload.did.as_str()
.and_then(|encryption| encryption.get_secret()), );
) {
if self.did.lock().as_str() != event.payload.did.as_str() { if self.did.lock().as_str() != event.payload.did.as_str() {
tracing::trace!("Did receive realtime event: {}", event); tracing::trace!("Did receive realtime event: {}", event);
let value = if event.payload.encrypt == 1 { let value = if event.payload.encrypt == 1 {
decrypt_bytes(event.payload.value, &secret).unwrap_or_default() match self
.encryption
.upgrade()
.and_then(|encryption| encryption.get_secret())
{
None => vec![],
Some(secret) => decrypt_bytes(event.payload.value, &secret).unwrap_or_default(),
}
} else { } else {
event.payload.value event.payload.value
}; };

View File

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use anyhow::Error; use anyhow::Error;
@ -18,7 +19,7 @@ use crate::entities::{
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserCloudConfig { pub struct UserCloudConfig {
pub enable_sync: bool, pub enable_sync: bool,
pub enable_encrypt: bool, enable_encrypt: bool,
// The secret used to encrypt the user's data // The secret used to encrypt the user's data
pub encrypt_secret: String, pub encrypt_secret: String,
} }
@ -31,6 +32,27 @@ impl UserCloudConfig {
encrypt_secret, encrypt_secret,
} }
} }
pub fn enable_encrypt(&self) -> bool {
self.enable_encrypt
}
pub fn with_enable_encrypt(mut self, enable_encrypt: bool) -> Self {
self.enable_encrypt = enable_encrypt;
// When the enable_encrypt is true, the encrypt_secret should not be empty
debug_assert!(!self.encrypt_secret.is_empty());
self
}
}
impl Display for UserCloudConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"enable_sync: {}, enable_encrypt: {}",
self.enable_sync, self.enable_encrypt
)
}
} }
/// Provide the generic interface for the user cloud service /// Provide the generic interface for the user cloud service

View File

@ -149,7 +149,7 @@ impl From<UserCloudConfig> for UserCloudConfigPB {
fn from(value: UserCloudConfig) -> Self { fn from(value: UserCloudConfig) -> Self {
Self { Self {
enable_sync: value.enable_sync, enable_sync: value.enable_sync,
enable_encrypt: value.enable_encrypt, enable_encrypt: value.enable_encrypt(),
encrypt_secret: value.encrypt_secret, encrypt_secret: value.encrypt_secret,
} }
} }

View File

@ -5,6 +5,7 @@ use serde_json::Value;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_sqlite::kv::StorePreferences; use flowy_sqlite::kv::StorePreferences;
use flowy_user_deps::cloud::UserCloudConfig;
use flowy_user_deps::entities::*; use flowy_user_deps::entities::*;
use lib_dispatch::prelude::*; use lib_dispatch::prelude::*;
use lib_infra::box_any::BoxAny; use lib_infra::box_any::BoxAny;
@ -12,7 +13,9 @@ use lib_infra::box_any::BoxAny;
use crate::entities::*; use crate::entities::*;
use crate::manager::UserManager; use crate::manager::UserManager;
use crate::notification::{send_notification, UserNotification}; use crate::notification::{send_notification, UserNotification};
use crate::services::cloud_config::{generate_cloud_config, get_cloud_config, save_cloud_config}; use crate::services::cloud_config::{
get_cloud_config, get_or_create_cloud_config, save_cloud_config,
};
fn upgrade_manager(manager: AFPluginState<Weak<UserManager>>) -> FlowyResult<Arc<UserManager>> { fn upgrade_manager(manager: AFPluginState<Weak<UserManager>>) -> FlowyResult<Arc<UserManager>> {
let manager = manager let manager = manager
@ -186,12 +189,6 @@ pub async fn set_encrypt_secret_handler(
let manager = upgrade_manager(manager)?; let manager = upgrade_manager(manager)?;
let store_preferences = upgrade_store_preferences(store_preferences)?; let store_preferences = upgrade_store_preferences(store_preferences)?;
let data = data.into_inner(); let data = data.into_inner();
let mut config = get_cloud_config(&store_preferences).unwrap_or_else(|| {
tracing::trace!("Generate default cloud config");
generate_cloud_config(&store_preferences)
});
match data.encryption_type { match data.encryption_type {
EncryptionTypePB::NoEncryption => { EncryptionTypePB::NoEncryption => {
tracing::error!("Encryption type is NoEncryption, but set encrypt secret"); tracing::error!("Encryption type is NoEncryption, but set encrypt secret");
@ -203,8 +200,7 @@ pub async fn set_encrypt_secret_handler(
&data.encryption_secret, &data.encryption_secret,
)?; )?;
config.encrypt_secret = data.encryption_secret; let config = UserCloudConfig::new(data.encryption_secret).with_enable_encrypt(true);
config.enable_encrypt = true;
manager manager
.set_encrypt_secret( .set_encrypt_secret(
data.user_id, data.user_id,
@ -212,10 +208,10 @@ pub async fn set_encrypt_secret_handler(
EncryptionType::SelfEncryption(data.encryption_sign), EncryptionType::SelfEncryption(data.encryption_sign),
) )
.await?; .await?;
save_cloud_config(data.user_id, &store_preferences, config)?;
}, },
} }
save_cloud_config(data.user_id, &store_preferences, config)?;
manager.resume_sign_up().await?; manager.resume_sign_up().await?;
Ok(()) Ok(())
} }
@ -250,9 +246,9 @@ pub async fn set_cloud_config_handler(
) -> Result<(), FlowyError> { ) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?; let manager = upgrade_manager(manager)?;
let session = manager.get_session()?; let session = manager.get_session()?;
let store_preferences = upgrade_store_preferences(store_preferences)?;
let update = data.into_inner(); let update = data.into_inner();
let mut config = get_cloud_config(&store_preferences) let store_preferences = upgrade_store_preferences(store_preferences)?;
let mut config = get_cloud_config(session.user_id, &store_preferences)
.ok_or(FlowyError::internal().context("Can't find any cloud config"))?; .ok_or(FlowyError::internal().context("Can't find any cloud config"))?;
if let Some(enable_sync) = update.enable_sync { if let Some(enable_sync) = update.enable_sync {
@ -261,20 +257,21 @@ pub async fn set_cloud_config_handler(
} }
if let Some(enable_encrypt) = update.enable_encrypt { if let Some(enable_encrypt) = update.enable_encrypt {
config.enable_encrypt = enable_encrypt; debug_assert!(enable_encrypt, "Disable encryption is not supported");
if enable_encrypt { if enable_encrypt {
tracing::info!("Enable encryption for user: {}", session.user_id);
config = config.with_enable_encrypt(enable_encrypt);
let encrypt_secret = config.encrypt_secret.clone();
// The encryption secret is generated when the user first enables encryption and will be // The encryption secret is generated when the user first enables encryption and will be
// used to validate the encryption secret is correct when the user logs in. // used to validate the encryption secret is correct when the user logs in.
let encryption_sign = let encryption_sign = manager.generate_encryption_sign(session.user_id, &encrypt_secret)?;
manager.generate_encryption_sign(session.user_id, &config.encrypt_secret)?;
let encryption_type = EncryptionType::SelfEncryption(encryption_sign); let encryption_type = EncryptionType::SelfEncryption(encryption_sign);
manager manager
.set_encrypt_secret( .set_encrypt_secret(session.user_id, encrypt_secret, encryption_type.clone())
session.user_id,
config.encrypt_secret.clone(),
encryption_type.clone(),
)
.await?; .await?;
save_cloud_config(session.user_id, &store_preferences, config.clone())?;
let params = let params =
UpdateUserProfileParams::new(session.user_id).with_encryption_type(encryption_type); UpdateUserProfileParams::new(session.user_id).with_encryption_type(encryption_type);
@ -282,8 +279,7 @@ pub async fn set_cloud_config_handler(
} }
} }
let config_pb = UserCloudConfigPB::from(config.clone()); let config_pb = UserCloudConfigPB::from(config);
save_cloud_config(session.user_id, &store_preferences, config)?;
send_notification( send_notification(
&session.user_id.to_string(), &session.user_id.to_string(),
UserNotification::DidUpdateCloudConfig, UserNotification::DidUpdateCloudConfig,
@ -295,12 +291,15 @@ pub async fn set_cloud_config_handler(
#[tracing::instrument(level = "debug", skip_all, err)] #[tracing::instrument(level = "debug", skip_all, err)]
pub async fn get_cloud_config_handler( pub async fn get_cloud_config_handler(
manager: AFPluginState<Weak<UserManager>>,
store_preferences: AFPluginState<Weak<StorePreferences>>, store_preferences: AFPluginState<Weak<StorePreferences>>,
) -> DataResult<UserCloudConfigPB, FlowyError> { ) -> DataResult<UserCloudConfigPB, FlowyError> {
let manager = upgrade_manager(manager)?;
let session = manager.get_session()?;
let store_preferences = upgrade_store_preferences(store_preferences)?; let store_preferences = upgrade_store_preferences(store_preferences)?;
// Generate the default config if the config is not exist // Generate the default config if the config is not exist
let config = get_cloud_config(&store_preferences) let config = get_or_create_cloud_config(session.user_id, &store_preferences);
.unwrap_or_else(|| generate_cloud_config(&store_preferences));
data_result_ok(config.into()) data_result_ok(config.into())
} }
@ -389,7 +388,6 @@ pub async fn open_historical_users_handler(
Ok(()) Ok(())
} }
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn push_realtime_event_handler( pub async fn push_realtime_event_handler(
payload: AFPluginData<RealtimePayloadPB>, payload: AFPluginData<RealtimePayloadPB>,
manager: AFPluginState<Weak<UserManager>>, manager: AFPluginState<Weak<UserManager>>,

View File

@ -6,7 +6,7 @@ use strum_macros::Display;
use flowy_derive::{Flowy_Event, ProtoBuf_Enum}; use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use flowy_user_deps::cloud::UserService; use flowy_user_deps::cloud::{UserCloudConfig, UserService};
use flowy_user_deps::entities::*; use flowy_user_deps::entities::*;
use lib_dispatch::prelude::*; use lib_dispatch::prelude::*;
use lib_infra::future::{to_fut, Fut}; use lib_infra::future::{to_fut, Fut};
@ -74,6 +74,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
fn did_init( fn did_init(
&self, &self,
user_id: i64, user_id: i64,
cloud_config: &Option<UserCloudConfig>,
user_workspace: &UserWorkspace, user_workspace: &UserWorkspace,
device_id: &str, device_id: &str,
) -> Fut<FlowyResult<()>>; ) -> Fut<FlowyResult<()>>;
@ -145,6 +146,7 @@ impl UserStatusCallback for DefaultUserStatusCallback {
fn did_init( fn did_init(
&self, &self,
_user_id: i64, _user_id: i64,
_cloud_config: &Option<UserCloudConfig>,
_user_workspace: &UserWorkspace, _user_workspace: &UserWorkspace,
_device_id: &str, _device_id: &str,
) -> Fut<FlowyResult<()>> { ) -> Fut<FlowyResult<()>> {

View File

@ -25,7 +25,7 @@ use crate::migrations::historical_document::HistoricalEmptyDocumentMigration;
use crate::migrations::local_user_to_cloud::migration_user_to_cloud; use crate::migrations::local_user_to_cloud::migration_user_to_cloud;
use crate::migrations::migration::UserLocalDataMigration; use crate::migrations::migration::UserLocalDataMigration;
use crate::migrations::MigrationUser; use crate::migrations::MigrationUser;
use crate::services::cloud_config::remove_cloud_config; use crate::services::cloud_config::get_cloud_config;
use crate::services::database::UserDB; use crate::services::database::UserDB;
use crate::services::entities::{ResumableSignUp, Session}; use crate::services::entities::{ResumableSignUp, Session};
use crate::services::user_awareness::UserAwarenessDataSource; use crate::services::user_awareness::UserAwarenessDataSource;
@ -121,8 +121,14 @@ impl UserManager {
self self
.initialize_user_awareness(&session, UserAwarenessDataSource::Local) .initialize_user_awareness(&session, UserAwarenessDataSource::Local)
.await; .await;
let cloud_config = get_cloud_config(session.user_id, &self.store_preferences);
if let Err(e) = user_status_callback if let Err(e) = user_status_callback
.did_init(session.user_id, &session.user_workspace, &session.device_id) .did_init(
session.user_id,
&cloud_config,
&session.user_workspace,
&session.device_id,
)
.await .await
{ {
tracing::error!("Failed to call did_init callback: {:?}", e); tracing::error!("Failed to call did_init callback: {:?}", e);
@ -213,7 +219,6 @@ impl UserManager {
auth_type: AuthType, auth_type: AuthType,
params: BoxAny, params: BoxAny,
) -> Result<UserProfile, FlowyError> { ) -> Result<UserProfile, FlowyError> {
remove_cloud_config(&self.store_preferences);
self.update_auth_type(&auth_type).await; self.update_auth_type(&auth_type).await;
let migration_user = self.get_migration_user(&auth_type).await; let migration_user = self.get_migration_user(&auth_type).await;
@ -325,7 +330,6 @@ impl UserManager {
let session = self.get_session()?; let session = self.get_session()?;
self.database.close(session.user_id)?; self.database.close(session.user_id)?;
self.set_current_session(None)?; self.set_current_session(None)?;
remove_cloud_config(&self.store_preferences);
let server = self.cloud_services.get_user_service()?; let server = self.cloud_services.get_user_service()?;
tokio::spawn(async move { tokio::spawn(async move {

View File

@ -7,41 +7,48 @@ use flowy_user_deps::cloud::UserCloudConfig;
const CLOUD_CONFIG_KEY: &str = "af_user_cloud_config"; const CLOUD_CONFIG_KEY: &str = "af_user_cloud_config";
pub fn generate_cloud_config(store_preference: &Arc<StorePreferences>) -> UserCloudConfig { fn generate_cloud_config(uid: i64, store_preference: &Arc<StorePreferences>) -> UserCloudConfig {
let config = UserCloudConfig::new(generate_encrypt_secret()); let config = UserCloudConfig::new(generate_encrypt_secret());
let key = cache_key_for_cloud_config(); let key = cache_key_for_cloud_config(uid);
store_preference.set_object(&key, config.clone()).unwrap(); store_preference.set_object(&key, config.clone()).unwrap();
config config
} }
pub fn remove_cloud_config(store_preference: &Arc<StorePreferences>) {
let key = cache_key_for_cloud_config();
store_preference.remove(&key);
}
pub fn save_cloud_config( pub fn save_cloud_config(
uid: i64, uid: i64,
store_preference: &Arc<StorePreferences>, store_preference: &Arc<StorePreferences>,
config: UserCloudConfig, config: UserCloudConfig,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let encrypt_secret = config.encrypt_secret.clone(); tracing::info!("save user:{} cloud config: {}", uid, config);
let key = cache_key_for_cloud_config(); let key = cache_key_for_cloud_config(uid);
store_preference.set_object(&key, config)?; store_preference.set_object(&key, config)?;
store_preference.set_object(&format!("{}-encrypt-secret", uid), encrypt_secret)?;
Ok(()) Ok(())
} }
fn cache_key_for_cloud_config() -> String { fn cache_key_for_cloud_config(uid: i64) -> String {
CLOUD_CONFIG_KEY.to_string() format!("{}:{}", CLOUD_CONFIG_KEY, uid)
} }
pub fn get_cloud_config(store_preference: &Arc<StorePreferences>) -> Option<UserCloudConfig> { pub fn get_cloud_config(
let key = cache_key_for_cloud_config(); uid: i64,
store_preference: &Arc<StorePreferences>,
) -> Option<UserCloudConfig> {
let key = cache_key_for_cloud_config(uid);
store_preference.get_object::<UserCloudConfig>(&key) store_preference.get_object::<UserCloudConfig>(&key)
} }
pub fn get_encrypt_secret(store_preference: &Arc<StorePreferences>) -> Option<String> { pub fn get_or_create_cloud_config(
let key = cache_key_for_cloud_config(); uid: i64,
store_preferences: &Arc<StorePreferences>,
) -> UserCloudConfig {
let key = cache_key_for_cloud_config(uid);
store_preferences
.get_object::<UserCloudConfig>(&key)
.unwrap_or_else(|| generate_cloud_config(uid, store_preferences))
}
pub fn get_encrypt_secret(uid: i64, store_preference: &Arc<StorePreferences>) -> Option<String> {
let key = cache_key_for_cloud_config(uid);
store_preference store_preference
.get_object::<UserCloudConfig>(&key) .get_object::<UserCloudConfig>(&key)
.map(|config| config.encrypt_secret) .map(|config| config.encrypt_secret)

View File

@ -37,7 +37,7 @@ impl UserManager {
"Failed to get store preference", "Failed to get store preference",
))?; ))?;
let encrypt_secret = get_encrypt_secret(&store_preference).ok_or(FlowyError::new( let encrypt_secret = get_encrypt_secret(uid, &store_preference).ok_or(FlowyError::new(
ErrorCode::Internal, ErrorCode::Internal,
"Encrypt secret is not set", "Encrypt secret is not set",
))?; ))?;