Skip to content

Commit 46e2c72

Browse files
authored
Merge pull request #1345 from AppFlowy-IO/mask-comment-name
feat: mask comment and reaction's email if profile name is not set
2 parents 55977ac + 82242e3 commit 46e2c72

7 files changed

+113
-36
lines changed

.sqlx/query-056174448a2ff0744b5943ba6d303b180ca9016cd26d284686f445c060cec4c5.json renamed to .sqlx/query-53d87db17bb9c1d002adc82ba9f2c07ff33ea987a1157d7f6fd2344091b98deb.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-b58432fffcf04a9485a7db5908c1801b34f51e51f3b06f679dc62e068e1cc721.json renamed to .sqlx/query-63f0871525ed70bd980223de574d241c0b738cfb7b0ea1fc808f02c0e05b9a2f.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-c5c72869f44067d90c3224a17ec0e32b10cdf9378947e2c7a8409e48423377eb.json renamed to .sqlx/query-95c00cd1ce7cdb8f5c8f45d5262d371b1b3c3f903f4eab9c0070d9916e3f8c12.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/database-entity/src/dto.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,9 +854,16 @@ pub struct AFWebUser {
854854
pub avatar_url: Option<String>,
855855
}
856856

857+
#[derive(Serialize, Deserialize, Debug, Clone)]
858+
pub struct AFWebUserWithObfuscatedName {
859+
pub uuid: Uuid,
860+
pub name: String,
861+
pub avatar_url: Option<String>,
862+
}
863+
857864
#[derive(Serialize, Deserialize, Debug)]
858865
pub struct GlobalComment {
859-
pub user: Option<AFWebUser>,
866+
pub user: Option<AFWebUserWithObfuscatedName>,
860867
pub created_at: DateTime<Utc>,
861868
pub last_updated_at: DateTime<Utc>,
862869
pub content: String,
@@ -885,7 +892,7 @@ pub struct Reactions {
885892
#[derive(Serialize, Deserialize, Debug)]
886893
pub struct Reaction {
887894
pub reaction_type: String,
888-
pub react_users: Vec<AFWebUser>,
895+
pub react_users: Vec<AFWebUserWithObfuscatedName>,
889896
pub comment_id: Uuid,
890897
}
891898

libs/database/src/pg_row.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use app_error::AppError;
33
use chrono::{DateTime, Utc};
44

55
use database_entity::dto::{
6-
AFAccessLevel, AFRole, AFUserProfile, AFWebUser, AFWorkspace, AFWorkspaceInvitationStatus,
7-
AccessRequestMinimal, AccessRequestStatus, AccessRequestWithViewId, AccessRequesterInfo,
8-
AccountLink, GlobalComment, QuickNote, Reaction, Template, TemplateCategory,
6+
AFAccessLevel, AFRole, AFUserProfile, AFWebUser, AFWebUserWithObfuscatedName, AFWorkspace,
7+
AFWorkspaceInvitationStatus, AccessRequestMinimal, AccessRequestStatus, AccessRequestWithViewId,
8+
AccessRequesterInfo, AccountLink, GlobalComment, QuickNote, Reaction, Template, TemplateCategory,
99
TemplateCategoryMinimal, TemplateCategoryType, TemplateCreator, TemplateCreatorMinimal,
1010
TemplateGroup, TemplateMinimal,
1111
};
@@ -330,8 +330,40 @@ impl From<AFWebUserColumn> for AFWebUser {
330330
}
331331
}
332332

333+
#[derive(sqlx::Type, Serialize, Debug)]
334+
pub struct AFWebUserWithEmailColumn {
335+
uuid: Uuid,
336+
name: String,
337+
email: String,
338+
avatar_url: Option<String>,
339+
}
340+
341+
fn mask_web_user_email(email: &str) -> String {
342+
email
343+
.split('@')
344+
.next()
345+
.map(|part| part.chars().take(6).collect())
346+
.unwrap_or_default()
347+
}
348+
349+
impl From<AFWebUserWithEmailColumn> for AFWebUserWithObfuscatedName {
350+
fn from(val: AFWebUserWithEmailColumn) -> Self {
351+
let obfuscated_name = if val.name == val.email {
352+
mask_web_user_email(&val.email)
353+
} else {
354+
val.name.clone()
355+
};
356+
357+
AFWebUserWithObfuscatedName {
358+
uuid: val.uuid,
359+
name: obfuscated_name,
360+
avatar_url: val.avatar_url,
361+
}
362+
}
363+
}
364+
333365
pub struct AFGlobalCommentRow {
334-
pub user: Option<AFWebUserColumn>,
366+
pub user: Option<AFWebUserWithEmailColumn>,
335367
pub created_at: DateTime<Utc>,
336368
pub last_updated_at: DateTime<Utc>,
337369
pub content: String,
@@ -358,7 +390,7 @@ impl From<AFGlobalCommentRow> for GlobalComment {
358390

359391
pub struct AFReactionRow {
360392
pub reaction_type: String,
361-
pub react_users: Vec<AFWebUserColumn>,
393+
pub react_users: Vec<AFWebUserWithEmailColumn>,
362394
pub comment_id: Uuid,
363395
}
364396

@@ -720,3 +752,23 @@ pub struct AFPublishViewWithPublishInfo {
720752
pub comments_enabled: bool,
721753
pub duplicate_enabled: bool,
722754
}
755+
756+
#[cfg(test)]
757+
mod tests {
758+
use super::*;
759+
760+
#[test]
761+
fn test_mask_web_user_email() {
762+
let name = "";
763+
let masked = mask_web_user_email(name);
764+
assert_eq!(masked, "");
765+
766+
let name = "[email protected]";
767+
let masked = mask_web_user_email(name);
768+
assert_eq!(masked, "john");
769+
770+
let name = "[email protected]";
771+
let masked = mask_web_user_email(name);
772+
assert_eq!(masked, "jonath");
773+
}
774+
}

libs/database/src/workspace.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use uuid::Uuid;
1111

1212
use crate::pg_row::{
1313
AFGlobalCommentRow, AFImportTask, AFPermissionRow, AFReactionRow, AFUserProfileRow,
14-
AFWebUserColumn, AFWorkspaceInvitationMinimal, AFWorkspaceMemberPermRow, AFWorkspaceMemberRow,
15-
AFWorkspaceRow,
14+
AFWebUserWithEmailColumn, AFWorkspaceInvitationMinimal, AFWorkspaceMemberPermRow,
15+
AFWorkspaceMemberRow, AFWorkspaceRow,
1616
};
1717
use crate::user::select_uid_from_email;
1818
use app_error::AppError;
@@ -1102,7 +1102,7 @@ pub async fn select_comments_for_published_view_ordered_by_recency<
11021102
avc.content,
11031103
avc.reply_comment_id,
11041104
avc.is_deleted,
1105-
(au.uuid, au.name, au.metadata ->> 'icon_url') AS "user: AFWebUserColumn",
1105+
(au.uuid, au.name, au.email, au.metadata ->> 'icon_url') AS "user: AFWebUserWithEmailColumn",
11061106
(NOT avc.is_deleted AND ($2 OR au.uuid = $3)) AS "can_be_deleted!"
11071107
FROM af_published_view_comment avc
11081108
LEFT OUTER JOIN af_user au ON avc.created_by = au.uid
@@ -1188,7 +1188,7 @@ pub async fn select_reactions_for_published_view_ordered_by_reaction_type_creati
11881188
SELECT
11891189
avr.comment_id,
11901190
avr.reaction_type,
1191-
ARRAY_AGG((au.uuid, au.name, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserColumn>"
1191+
ARRAY_AGG((au.uuid, au.name, au.email, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserWithEmailColumn>"
11921192
FROM af_published_view_reaction avr
11931193
INNER JOIN af_user au ON avr.created_by = au.uid
11941194
WHERE view_id = $1
@@ -1216,7 +1216,7 @@ pub async fn select_reactions_for_comment_ordered_by_reaction_type_creation_time
12161216
r#"
12171217
SELECT
12181218
avr.reaction_type,
1219-
ARRAY_AGG((au.uuid, au.name, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserColumn>",
1219+
ARRAY_AGG((au.uuid, au.name, au.email, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserWithEmailColumn>",
12201220
avr.comment_id
12211221
FROM af_published_view_reaction avr
12221222
INNER JOIN af_user au ON avr.created_by = au.uid

tests/workspace/publish.rs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use collab_entity::CollabType;
1818
use collab_folder::{CollabOrigin, Folder, UserId};
1919
use itertools::Itertools;
2020
use serde::{Deserialize, Serialize};
21+
use shared_entity::dto::auth_dto::UpdateUserParams;
2122
use shared_entity::dto::publish_dto::PublishDatabaseData;
2223
use std::collections::{HashMap, HashSet};
2324
use std::sync::Arc;
@@ -471,13 +472,22 @@ async fn test_publish_doc() {
471472

472473
#[tokio::test]
473474
async fn test_publish_comments() {
474-
let (page_owner_client, page_owner) = generate_unique_registered_user_client().await;
475+
let (page_owner_client, _) = generate_unique_registered_user_client().await;
475476
let workspace_id = get_first_workspace(&page_owner_client).await;
476477
let published_view_namespace = Uuid::new_v4().to_string();
477478
page_owner_client
478479
.set_workspace_publish_namespace(&workspace_id, published_view_namespace)
479480
.await
480481
.unwrap();
482+
page_owner_client
483+
.update_user(UpdateUserParams {
484+
name: Some("PageOwner".to_string()),
485+
password: None,
486+
email: None,
487+
metadata: None,
488+
})
489+
.await
490+
.unwrap();
481491

482492
let publish_name = "published-view";
483493
let view_id = Uuid::new_v4();
@@ -516,14 +526,23 @@ async fn test_publish_comments() {
516526
assert_eq!(comments[0].content, page_owner_comment_content);
517527
}
518528

519-
let (first_user_client, first_user) = generate_unique_registered_user_client().await;
529+
let (first_user_client, _) = generate_unique_registered_user_client().await;
520530
let first_user_comment_content = "comment from first authenticated user";
521531
// This is to ensure that the second comment creation timestamp is later than the first one
522532
sleep(Duration::from_millis(1));
523533
first_user_client
524534
.create_comment_on_published_view(&view_id, first_user_comment_content, &None)
525535
.await
526536
.unwrap();
537+
first_user_client
538+
.update_user(UpdateUserParams {
539+
name: Some("User1".to_string()),
540+
password: None,
541+
email: None,
542+
metadata: None,
543+
})
544+
.await
545+
.unwrap();
527546
let guest_client = localhost_client();
528547
let result = guest_client
529548
.create_comment_on_published_view(&view_id, "comment from anonymous", &None)
@@ -571,10 +590,7 @@ async fn test_publish_comments() {
571590
.unwrap_or("".to_string())
572591
})
573592
.collect_vec();
574-
assert_eq!(
575-
comment_creators,
576-
vec![first_user.email.clone(), page_owner.email.clone()]
577-
);
593+
assert_eq!(comment_creators, vec!["User1", "PageOwner"]);
578594
let comment_content = published_view_comments
579595
.iter()
580596
.map(|c| c.content.clone())
@@ -586,7 +602,16 @@ async fn test_publish_comments() {
586602

587603
// Test if it's possible to reply to another user's comment
588604
let second_user_comment_content = "comment from second authenticated user";
589-
let (second_user_client, second_user) = generate_unique_registered_user_client().await;
605+
let (second_user_client, _) = generate_unique_registered_user_client().await;
606+
second_user_client
607+
.update_user(UpdateUserParams {
608+
name: Some("User2".to_string()),
609+
password: None,
610+
email: None,
611+
metadata: None,
612+
})
613+
.await
614+
.unwrap();
590615
{
591616
let published_view_comments: Vec<GlobalComment> = guest_client
592617
.get_published_view_comments(&view_id)
@@ -626,14 +651,7 @@ async fn test_publish_comments() {
626651
.unwrap_or("".to_string())
627652
})
628653
.collect_vec();
629-
assert_eq!(
630-
comment_creators,
631-
vec![
632-
second_user.email.clone(),
633-
first_user.email.clone(),
634-
page_owner.email.clone()
635-
]
636-
);
654+
assert_eq!(comment_creators, vec!["User2", "User1", "PageOwner"]);
637655
assert_eq!(
638656
published_view_comments[0].reply_comment_id,
639657
Some(published_view_comments[1].comment_id)

0 commit comments

Comments
 (0)