diff --git a/src/api/client/membership/invite.rs b/src/api/client/membership/invite.rs index 018fb774b..9526cec97 100644 --- a/src/api/client/membership/invite.rs +++ b/src/api/client/membership/invite.rs @@ -48,14 +48,13 @@ pub(crate) async fn invite_user_route( match &body.recipient { | invite_user::v3::InvitationRecipient::UserId { user_id } => { - let sender_ignored_recipient = services.users.user_is_ignored(sender_user, user_id); - let recipient_ignored_by_sender = - services.users.user_is_ignored(user_id, sender_user); + let sender_filter_level = services.users.user_filter_level(sender_user, user_id); + let recipient_filter_level = services.users.user_filter_level(user_id, sender_user); - let (sender_ignored_recipient, recipient_ignored_by_sender) = - join!(sender_ignored_recipient, recipient_ignored_by_sender); + let (sender_filter_level, recipient_filter_level) = + join!(sender_filter_level, recipient_filter_level); - if sender_ignored_recipient { + if !sender_filter_level.allowed() { return Ok(invite_user::v3::Response {}); } @@ -70,7 +69,7 @@ pub(crate) async fn invite_user_route( } } - if recipient_ignored_by_sender { + if !recipient_filter_level.allowed() { // silently drop the invite to the recipient if they've been ignored by the // sender, pretend it worked return Ok(invite_user::v3::Response {}); diff --git a/src/api/client/message.rs b/src/api/client/message.rs index 842036f5c..dea95713e 100644 --- a/src/api/client/message.rs +++ b/src/api/client/message.rs @@ -286,11 +286,12 @@ where if ignored_type && (ignored_server - || (!services.config.send_messages_from_ignored_users_to_client - && services + || !(services.config.send_messages_from_ignored_users_to_client + || services .users - .user_is_ignored(event.sender(), user_id) - .await)) + .user_filter_level(event.sender(), user_id) + .await + .allowed())) { return true; } diff --git a/src/api/client/room/create.rs b/src/api/client/room/create.rs index 238691d14..0d3425a7c 100644 --- a/src/api/client/room/create.rs +++ b/src/api/client/room/create.rs @@ -223,9 +223,20 @@ pub(crate) async fn create_room_route( if preset == RoomPreset::TrustedPrivateChat { for invite in &body.invite { - if services.users.user_is_ignored(sender_user, invite).await { + if !services + .users + .user_filter_level(sender_user, invite) + .await + .allowed() + { + // don't send invites to users the sender has ignored continue; - } else if services.users.user_is_ignored(invite, sender_user).await { + } else if !services + .users + .user_filter_level(invite, sender_user) + .await + .allowed() + { // silently drop the invite to the recipient if they've been ignored by the // sender, pretend it worked continue; @@ -400,9 +411,20 @@ pub(crate) async fn create_room_route( // 8. Events implied by invite (and TODO: invite_3pid) drop(state_lock); for user_id in &body.invite { - if services.users.user_is_ignored(sender_user, user_id).await { + if !services + .users + .user_filter_level(sender_user, user_id) + .await + .allowed() + { + // don't send invites to users the sender has ignored continue; - } else if services.users.user_is_ignored(user_id, sender_user).await { + } else if !services + .users + .user_filter_level(user_id, sender_user) + .await + .allowed() + { // silently drop the invite to the recipient if they've been ignored by the // sender, pretend it worked continue; diff --git a/src/api/client/sync/v3.rs b/src/api/client/sync/v3.rs index 298a6e4b3..6fc61e06c 100644 --- a/src/api/client/sync/v3.rs +++ b/src/api/client/sync/v3.rs @@ -624,9 +624,10 @@ async fn load_joined_room( .filter_map(|(read_user, _, edu)| async move { services .users - .user_is_ignored(read_user, sender_user) + .user_filter_level(read_user, sender_user) .await - .or_some((read_user.to_owned(), edu)) + .allowed() + .then_some((read_user.to_owned(), edu)) }) .collect::>>() .map(Ok); diff --git a/src/api/client/sync/v4.rs b/src/api/client/sync/v4.rs index a16e4526d..78e1cb890 100644 --- a/src/api/client/sync/v4.rs +++ b/src/api/client/sync/v4.rs @@ -558,9 +558,10 @@ pub(crate) async fn sync_events_v4_route( .filter_map(|(read_user, _ts, v)| async move { services .users - .user_is_ignored(read_user, sender_user) + .user_filter_level(read_user, sender_user) .await - .or_some(v) + .allowed() + .then_some(v) }) .collect() .await; diff --git a/src/api/client/sync/v5.rs b/src/api/client/sync/v5.rs index e4cefba0f..66ca8a732 100644 --- a/src/api/client/sync/v5.rs +++ b/src/api/client/sync/v5.rs @@ -455,9 +455,10 @@ where .filter_map(|(read_user, _ts, v)| async move { services .users - .user_is_ignored(read_user, sender_user) + .user_filter_level(read_user, sender_user) .await - .or_some(v) + .allowed() + .then_some(v) }) .collect() .await; diff --git a/src/service/rooms/state_cache/update.rs b/src/service/rooms/state_cache/update.rs index 86c1afe72..a681cbdd3 100644 --- a/src/service/rooms/state_cache/update.rs +++ b/src/service/rooms/state_cache/update.rs @@ -122,7 +122,13 @@ pub async fn update_membership( }, | MembershipState::Invite => { // We want to know if the sender is ignored by the receiver - if self.services.users.user_is_ignored(sender, user_id).await { + if !self + .services + .users + .user_filter_level(sender, user_id) + .await + .allowed() + { return Ok(()); } diff --git a/src/service/rooms/timeline/append.rs b/src/service/rooms/timeline/append.rs index 1d404e8ae..fe0f91c2d 100644 --- a/src/service/rooms/timeline/append.rs +++ b/src/service/rooms/timeline/append.rs @@ -193,9 +193,9 @@ where .state_cache .active_local_users_in_room(pdu.room_id()) .map(ToOwned::to_owned) - // Don't notify the sender of their own events, and dont send from ignored users + // Don't notify the sender of their own events, and don't send from ignored users .ready_filter(|user| *user != pdu.sender()) - .filter_map(|recipient_user| async move { (!self.services.users.user_is_ignored(pdu.sender(), &recipient_user).await).then_some(recipient_user) }) + .filter_map(|recipient_user| async move { (self.services.users.user_filter_level(pdu.sender(), &recipient_user).await.allowed()).then_some(recipient_user) }) .collect() .await; diff --git a/src/service/rooms/typing/mod.rs b/src/service/rooms/typing/mod.rs index a81ee95c7..3b667fcc8 100644 --- a/src/service/rooms/typing/mod.rs +++ b/src/service/rooms/typing/mod.rs @@ -12,7 +12,11 @@ use ruma::{ }; use tokio::sync::{RwLock, broadcast}; -use crate::{Dep, globals, sending, sending::EduBuf, users}; +use crate::{ + Dep, globals, + sending::{self, EduBuf}, + users::{self}, +}; pub struct Service { server: Arc, @@ -197,11 +201,11 @@ impl Service { .into_keys() .stream() .filter_map(|typing_user_id| async move { - (!self - .services + self.services .users - .user_is_ignored(&typing_user_id, sender_user) - .await) + .user_filter_level(&typing_user_id, sender_user) + .await + .allowed() .then_some(typing_user_id) }) .collect() diff --git a/src/service/users/mod.rs b/src/service/users/mod.rs index 6ddd8d798..d12006e6c 100644 --- a/src/service/users/mod.rs +++ b/src/service/users/mod.rs @@ -39,6 +39,22 @@ pub struct UserSuspension { pub suspended_by: String, } +/// Represents a user's level of filtering on actions from another user. +/// "Ignore" and "block" are defined in [MSC4283]. +/// This may be moved into Ruwuma in the future. +/// +/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UserFilterLevel { + Allow, + Ignore, + Block, +} + +impl UserFilterLevel { + pub fn allowed(&self) -> bool { return *self == UserFilterLevel::Allow; } +} + pub struct Service { services: Services, db: Data, @@ -123,20 +139,32 @@ impl crate::Service for Service { } impl Service { - /// Returns true/false based on whether the recipient/receiving user has - /// blocked the sender - pub async fn user_is_ignored(&self, sender_user: &UserId, recipient_user: &UserId) -> bool { + /// Returns a UserActionResult based on whether the recipient/receiving user + /// has ignored or blocked the sender + pub async fn user_filter_level( + &self, + sender_user: &UserId, + recipient_user: &UserId, + ) -> UserFilterLevel { + // can only return Allow or Ignore for now self.services .account_data .get_global(recipient_user, GlobalAccountDataEventType::IgnoredUserList) .await - .is_ok_and(|ignored: IgnoredUserListEvent| { - ignored + .map(|ignored: IgnoredUserListEvent| { + let sender_ignored_recipient = ignored .content .ignored_users .keys() - .any(|blocked_user| blocked_user == sender_user) + .any(|blocked_user| blocked_user == sender_user); + + if sender_ignored_recipient { + UserFilterLevel::Ignore + } else { + UserFilterLevel::Allow + } }) + .unwrap_or(UserFilterLevel::Allow) } /// Check if a user is an admin