Avoid leaking private document in search endpoint (#1113)
* fix: auto patch workspace folder without space * fix: avoid leaking private document contents in search endpoint --------- Co-authored-by: weidong fu <nathan@appflowy.io>
This commit is contained in:
parent
ab0fa6e7fc
commit
327e184e28
|
|
@ -32,6 +32,7 @@ async fn document_search(
|
||||||
let metrics = &*state.metrics.request_metrics;
|
let metrics = &*state.metrics.request_metrics;
|
||||||
let resp = search_document(
|
let resp = search_document(
|
||||||
&state.pg_pool,
|
&state.pg_pool,
|
||||||
|
&state.collab_access_control_storage,
|
||||||
&state.indexer_scheduler,
|
&state.indexer_scheduler,
|
||||||
uid,
|
uid,
|
||||||
workspace_id,
|
workspace_id,
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,13 @@ use shared_entity::dto::workspace_dto::{
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Return all folders belonging to a workspace, excluding private sections which the user does not have access to.
|
pub struct PrivateAndNonviewableViews {
|
||||||
pub fn collab_folder_to_folder_view(
|
pub my_private_view_ids: HashSet<String>,
|
||||||
workspace_id: Uuid,
|
pub nonviewable_view_ids: HashSet<String>,
|
||||||
root_view_id: &str,
|
}
|
||||||
folder: &Folder,
|
|
||||||
max_depth: u32,
|
pub fn private_and_nonviewable_view_ids(folder: &Folder) -> PrivateAndNonviewableViews {
|
||||||
pubished_view_ids: &HashSet<String>,
|
let mut nonviewable_view_ids = HashSet::new();
|
||||||
) -> Result<FolderView, AppError> {
|
|
||||||
let mut unviewable = HashSet::new();
|
|
||||||
let mut my_private_view_ids = HashSet::new();
|
let mut my_private_view_ids = HashSet::new();
|
||||||
for private_section in folder.get_my_private_sections() {
|
for private_section in folder.get_my_private_sections() {
|
||||||
my_private_view_ids.insert(private_section.id);
|
my_private_view_ids.insert(private_section.id);
|
||||||
|
|
@ -26,21 +24,36 @@ pub fn collab_folder_to_folder_view(
|
||||||
if let Some(private_view) = folder.get_view(&private_section.id) {
|
if let Some(private_view) = folder.get_view(&private_section.id) {
|
||||||
if check_if_view_is_space(&private_view) && !my_private_view_ids.contains(&private_section.id)
|
if check_if_view_is_space(&private_view) && !my_private_view_ids.contains(&private_section.id)
|
||||||
{
|
{
|
||||||
unviewable.insert(private_section.id);
|
nonviewable_view_ids.insert(private_section.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for trash_view in folder.get_all_trash_sections() {
|
for trash_view in folder.get_all_trash_sections() {
|
||||||
unviewable.insert(trash_view.id);
|
nonviewable_view_ids.insert(trash_view.id);
|
||||||
}
|
}
|
||||||
|
PrivateAndNonviewableViews {
|
||||||
|
my_private_view_ids,
|
||||||
|
nonviewable_view_ids,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return all folders belonging to a workspace, excluding private sections which the user does not have access to.
|
||||||
|
pub fn collab_folder_to_folder_view(
|
||||||
|
workspace_id: Uuid,
|
||||||
|
root_view_id: &str,
|
||||||
|
folder: &Folder,
|
||||||
|
max_depth: u32,
|
||||||
|
pubished_view_ids: &HashSet<String>,
|
||||||
|
) -> Result<FolderView, AppError> {
|
||||||
|
let private_and_nonviewable_views = private_and_nonviewable_view_ids(folder);
|
||||||
|
|
||||||
to_folder_view(
|
to_folder_view(
|
||||||
workspace_id,
|
workspace_id,
|
||||||
"",
|
"",
|
||||||
root_view_id,
|
root_view_id,
|
||||||
folder,
|
folder,
|
||||||
&unviewable,
|
&private_and_nonviewable_views.nonviewable_view_ids,
|
||||||
&my_private_view_ids,
|
&private_and_nonviewable_views.my_private_view_ids,
|
||||||
pubished_view_ids,
|
pubished_view_ids,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
|
|
@ -227,6 +240,27 @@ pub fn section_items_to_trash_folder_view(
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_if_view_ancestors_fulfil_condition(
|
||||||
|
view_id: &str,
|
||||||
|
collab_folder: &Folder,
|
||||||
|
condition: impl Fn(&collab_folder::View) -> bool,
|
||||||
|
) -> bool {
|
||||||
|
let mut current_view_id = view_id.to_string();
|
||||||
|
loop {
|
||||||
|
let view = match collab_folder.get_view(¤t_view_id) {
|
||||||
|
Some(view) => view,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if condition(&view) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current_view_id = view.parent_view_id.clone();
|
||||||
|
if current_view_id.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_if_view_is_space(view: &collab_folder::View) -> bool {
|
pub fn check_if_view_is_space(view: &collab_folder::View) -> bool {
|
||||||
let extra = match view.extra.as_ref() {
|
let extra = match view.extra.as_ref() {
|
||||||
Some(extra) => extra,
|
Some(extra) => extra,
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,9 @@ use collab_document::document::Document;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use collab_entity::EncodedCollab;
|
use collab_entity::EncodedCollab;
|
||||||
use collab_folder::hierarchy_builder::NestedChildViewBuilder;
|
use collab_folder::hierarchy_builder::NestedChildViewBuilder;
|
||||||
use collab_folder::CollabOrigin;
|
|
||||||
use collab_folder::Folder;
|
use collab_folder::Folder;
|
||||||
use collab_folder::SectionItem;
|
use collab_folder::SectionItem;
|
||||||
use collab_folder::SpaceInfo;
|
use collab_folder::{CollabOrigin, SpaceInfo};
|
||||||
use collab_rt_entity::user::RealtimeUser;
|
use collab_rt_entity::user::RealtimeUser;
|
||||||
use database::collab::select_last_updated_database_row_ids;
|
use database::collab::select_last_updated_database_row_ids;
|
||||||
use database::collab::select_workspace_database_oid;
|
use database::collab::select_workspace_database_oid;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
use crate::api::metrics::RequestMetrics;
|
use crate::api::metrics::RequestMetrics;
|
||||||
|
use crate::biz::collab::folder_view::{
|
||||||
|
check_if_view_ancestors_fulfil_condition, private_and_nonviewable_view_ids,
|
||||||
|
};
|
||||||
|
use crate::biz::collab::utils::get_latest_collab_folder;
|
||||||
use app_error::ErrorCode;
|
use app_error::ErrorCode;
|
||||||
use appflowy_ai_client::dto::{
|
use appflowy_ai_client::dto::{
|
||||||
EmbeddingEncodingFormat, EmbeddingInput, EmbeddingModel, EmbeddingOutput, EmbeddingRequest,
|
EmbeddingEncodingFormat, EmbeddingInput, EmbeddingModel, EmbeddingOutput, EmbeddingRequest,
|
||||||
};
|
};
|
||||||
|
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
|
||||||
|
use database::collab::GetCollabOrigin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use database::index::{search_documents, SearchDocumentParams};
|
use database::index::{search_documents, SearchDocumentParams};
|
||||||
|
|
@ -17,6 +23,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
pub async fn search_document(
|
pub async fn search_document(
|
||||||
pg_pool: &PgPool,
|
pg_pool: &PgPool,
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
indexer_scheduler: &Arc<IndexerScheduler>,
|
indexer_scheduler: &Arc<IndexerScheduler>,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
workspace_id: Uuid,
|
workspace_id: Uuid,
|
||||||
|
|
@ -75,8 +82,23 @@ pub async fn search_document(
|
||||||
results.len(),
|
results.len(),
|
||||||
request.query
|
request.query
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let folder = get_latest_collab_folder(
|
||||||
|
collab_storage,
|
||||||
|
GetCollabOrigin::User { uid },
|
||||||
|
&workspace_id.to_string(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let private_and_nonviewable_views = private_and_nonviewable_view_ids(&folder);
|
||||||
|
let non_searchable_view_ids = private_and_nonviewable_views.nonviewable_view_ids;
|
||||||
|
let filtered_results = results.into_iter().filter(|item| {
|
||||||
|
!check_if_view_ancestors_fulfil_condition(&item.object_id, &folder, |view| {
|
||||||
|
non_searchable_view_ids.contains(&view.id)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
results
|
filtered_results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| SearchDocumentResponseItem {
|
.map(|item| SearchDocumentResponseItem {
|
||||||
object_id: item.object_id,
|
object_id: item.object_id,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue