mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
23a34af30f
commit
de01bf70cd
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
@ -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()?;
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)?;
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>>,
|
||||||
|
@ -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<()>> {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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",
|
||||||
))?;
|
))?;
|
||||||
|
Loading…
Reference in New Issue
Block a user