diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 1a183f2b..f0272532 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -480,7 +480,7 @@ # Maximum number of spaces to cache role data for. When exceeded the # cache is cleared and repopulated on demand. # -#space_roles_cache_capacity = 1000 +#space_roles_cache_flush_threshold = 1000 # Enabling this setting opens registration to anyone without restrictions. # This makes your server vulnerable to abuse diff --git a/src/admin/space/roles.rs b/src/admin/space/roles.rs index a3f25cec..eb0a5d5a 100644 --- a/src/admin/space/roles.rs +++ b/src/admin/space/roles.rs @@ -13,9 +13,27 @@ use serde_json::value::to_raw_value; use crate::{admin_command, admin_command_dispatch}; -macro_rules! resolve_space { +macro_rules! resolve_room_as_space { ($self:expr, $space:expr) => {{ let space_id = $self.services.rooms.alias.resolve(&$space).await?; + if !matches!( + $self + .services + .rooms + .state_accessor + .get_room_type(&space_id) + .await, + Ok(ruma::room::RoomType::Space) + ) { + return Err!("The specified room is not a Space."); + } + space_id + }}; +} + +macro_rules! resolve_space { + ($self:expr, $space:expr) => {{ + let space_id = resolve_room_as_space!($self, $space); if !$self .services .rooms @@ -31,17 +49,6 @@ macro_rules! resolve_space { ) .await; } - if !matches!( - $self - .services - .rooms - .state_accessor - .get_room_type(&space_id) - .await, - Ok(ruma::room::RoomType::Space) - ) { - return Err!("The specified room is not a Space."); - } space_id }}; } @@ -59,6 +66,24 @@ macro_rules! custom_state_pdu { }; } +macro_rules! send_space_state { + ($self:expr, $space_id:expr, $event_type:expr, $state_key:expr, $content:expr) => {{ + let state_lock = $self.services.rooms.state.mutex.lock(&$space_id).await; + let server_user = &$self.services.globals.server_user; + $self + .services + .rooms + .timeline + .build_and_append_pdu( + custom_state_pdu!($event_type, $state_key, $content), + server_user, + Some(&$space_id), + &state_lock, + ) + .await? + }}; +} + #[admin_command_dispatch] #[derive(Debug, Subcommand)] pub enum SpaceRolesCommand { @@ -189,19 +214,7 @@ async fn add( power_level, }); - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_ROLES_EVENT_TYPE, "", &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_ROLES_EVENT_TYPE, "", &content); self.write_str(&format!("Added role '{role_name}' to space {space_id}.")) .await @@ -224,22 +237,10 @@ async fn remove(&self, space: OwnedRoomOrAliasId, role_name: String) -> Result { return Err!("Role '{role_name}' does not exist in this space."); } - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; + send_space_state!(self, space_id, SPACE_ROLES_EVENT_TYPE, "", &content); - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_ROLES_EVENT_TYPE, "", &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; - - // Cascade: remove the role from all users' member events let member_event_type = StateEventType::from(SPACE_ROLE_MEMBER_EVENT_TYPE.to_owned()); + let server_user = &self.services.globals.server_user; if let Ok(shortstatehash) = self .services .rooms @@ -247,6 +248,7 @@ async fn remove(&self, space: OwnedRoomOrAliasId, role_name: String) -> Result { .get_room_shortstatehash(&space_id) .await { + let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; let user_entries: Vec<(_, ruma::OwnedEventId)> = self .services .rooms @@ -357,19 +359,7 @@ async fn assign( content.roles.push(role_name.clone()); - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_ROLE_MEMBER_EVENT_TYPE, user_id.as_str(), &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_ROLE_MEMBER_EVENT_TYPE, user_id.as_str(), &content); self.write_str(&format!("Assigned role '{role_name}' to {user_id} in space {space_id}.")) .await @@ -400,19 +390,7 @@ async fn revoke( return Err!("User {user_id} does not have role '{role_name}' in this space."); } - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_ROLE_MEMBER_EVENT_TYPE, user_id.as_str(), &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_ROLE_MEMBER_EVENT_TYPE, user_id.as_str(), &content); self.write_str(&format!("Revoked role '{role_name}' from {user_id} in space {space_id}.")) .await @@ -456,19 +434,7 @@ async fn require( content.required_roles.push(role_name.clone()); - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_ROLE_ROOM_EVENT_TYPE, room_id.as_str(), &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_ROLE_ROOM_EVENT_TYPE, room_id.as_str(), &content); self.write_str(&format!( "Room {room_id} now requires role '{role_name}' in space {space_id}." @@ -501,19 +467,7 @@ async fn unrequire( return Err!("Room {room_id} does not require role '{role_name}' in this space."); } - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_ROLE_ROOM_EVENT_TYPE, room_id.as_str(), &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_ROLE_ROOM_EVENT_TYPE, room_id.as_str(), &content); self.write_str(&format!( "Removed role requirement '{role_name}' from room {room_id} in space {space_id}." @@ -581,32 +535,10 @@ async fn room(&self, space: OwnedRoomOrAliasId, room_id: OwnedRoomId) -> Result #[admin_command] async fn enable(&self, space: OwnedRoomOrAliasId) -> Result { - let space_id = self.services.rooms.alias.resolve(&space).await?; - if !matches!( - self.services - .rooms - .state_accessor - .get_room_type(&space_id) - .await, - Ok(ruma::room::RoomType::Space) - ) { - return Err!("The specified room is not a Space."); - } + let space_id = resolve_room_as_space!(self, space); let content = SpaceCascadingEventContent { enabled: true }; - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_CASCADING_EVENT_TYPE, "", &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_CASCADING_EVENT_TYPE, "", &content); self.services .rooms @@ -620,32 +552,10 @@ async fn enable(&self, space: OwnedRoomOrAliasId) -> Result { #[admin_command] async fn disable(&self, space: OwnedRoomOrAliasId) -> Result { - let space_id = self.services.rooms.alias.resolve(&space).await?; - if !matches!( - self.services - .rooms - .state_accessor - .get_room_type(&space_id) - .await, - Ok(ruma::room::RoomType::Space) - ) { - return Err!("The specified room is not a Space."); - } + let space_id = resolve_room_as_space!(self, space); let content = SpaceCascadingEventContent { enabled: false }; - let state_lock = self.services.rooms.state.mutex.lock(&space_id).await; - let server_user = &self.services.globals.server_user; - - self.services - .rooms - .timeline - .build_and_append_pdu( - custom_state_pdu!(SPACE_CASCADING_EVENT_TYPE, "", &content), - server_user, - Some(&space_id), - &state_lock, - ) - .await?; + send_space_state!(self, space_id, SPACE_CASCADING_EVENT_TYPE, "", &content); self.write_str(&format!("Space permission cascading disabled for {space_id}.")) .await @@ -653,17 +563,7 @@ async fn disable(&self, space: OwnedRoomOrAliasId) -> Result { #[admin_command] async fn status(&self, space: OwnedRoomOrAliasId) -> Result { - let space_id = self.services.rooms.alias.resolve(&space).await?; - if !matches!( - self.services - .rooms - .state_accessor - .get_room_type(&space_id) - .await, - Ok(ruma::room::RoomType::Space) - ) { - return Err!("The specified room is not a Space."); - } + let space_id = resolve_room_as_space!(self, space); let global_default = self.services.rooms.roles.is_enabled(); let cascading_event_type = StateEventType::from(SPACE_CASCADING_EVENT_TYPE.to_owned()); diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 7fea89a3..1239b805 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -616,8 +616,8 @@ pub struct Config { /// cache is cleared and repopulated on demand. /// /// default: 1000 - #[serde(default = "default_space_roles_cache_capacity")] - pub space_roles_cache_capacity: u32, + #[serde(default = "default_space_roles_cache_flush_threshold")] + pub space_roles_cache_flush_threshold: u32, /// Enabling this setting opens registration to anyone without restrictions. /// This makes your server vulnerable to abuse @@ -2843,4 +2843,4 @@ fn default_ldap_uid_attribute() -> String { String::from("uid") } fn default_ldap_name_attribute() -> String { String::from("givenName") } -fn default_space_roles_cache_capacity() -> u32 { 1000 } +fn default_space_roles_cache_flush_threshold() -> u32 { 1000 } diff --git a/src/core/matrix/space_roles.rs b/src/core/matrix/space_roles.rs index 3d4f6e5b..dc83c604 100644 --- a/src/core/matrix/space_roles.rs +++ b/src/core/matrix/space_roles.rs @@ -52,7 +52,6 @@ mod tests { let content = SpaceRolesEventContent { roles }; let json = serde_json::to_string(&content).unwrap(); let deserialized: SpaceRolesEventContent = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized.roles.len(), 2); assert_eq!(deserialized.roles["admin"].power_level, Some(100)); assert!(deserialized.roles["nsfw"].power_level.is_none()); } @@ -79,16 +78,4 @@ mod tests { let json = r#"{"power_level":100}"#; serde_json::from_str::(json).unwrap_err(); } - - #[test] - fn wrong_type_for_roles_fails() { - let json = r#"{"roles":"not_an_array"}"#; - serde_json::from_str::(json).unwrap_err(); - } - - #[test] - fn wrong_type_for_required_roles_fails() { - let json = r#"{"required_roles":42}"#; - serde_json::from_str::(json).unwrap_err(); - } } diff --git a/src/service/rooms/roles/cache_tests.rs b/src/service/rooms/roles/cache_tests.rs deleted file mode 100644 index 31fece94..00000000 --- a/src/service/rooms/roles/cache_tests.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::collections::{BTreeMap, HashMap, HashSet}; - -use conduwuit_core::matrix::space_roles::RoleDefinition; -use ruma::{OwnedRoomId, OwnedUserId, room_id, user_id}; - -use super::{ - compute_user_power_level, roles_satisfy_requirements, - tests::{make_roles, make_set}, -}; - -struct MockCache { - roles: HashMap>, - user_roles: HashMap>>, - room_requirements: HashMap>>, - room_to_space: HashMap>, -} - -impl MockCache { - fn new() -> Self { - Self { - roles: HashMap::new(), - user_roles: HashMap::new(), - room_requirements: HashMap::new(), - room_to_space: HashMap::new(), - } - } - - fn add_space(&mut self, space: OwnedRoomId, roles: BTreeMap) { - self.roles.insert(space, roles); - } - - fn add_child(&mut self, space: &OwnedRoomId, child: OwnedRoomId) { - self.room_to_space - .entry(child) - .or_default() - .insert(space.clone()); - } - - fn assign_role(&mut self, space: &OwnedRoomId, user: OwnedUserId, role: String) { - self.user_roles - .entry(space.clone()) - .or_default() - .entry(user) - .or_default() - .insert(role); - } - - fn revoke_role(&mut self, space: &OwnedRoomId, user: &OwnedUserId, role: &str) { - if let Some(space_users) = self.user_roles.get_mut(space) { - if let Some(user_roles) = space_users.get_mut(user) { - user_roles.remove(role); - } - } - } - - fn set_room_requirements( - &mut self, - space: &OwnedRoomId, - room: OwnedRoomId, - reqs: HashSet, - ) { - self.room_requirements - .entry(space.clone()) - .or_default() - .insert(room, reqs); - } - - fn user_qualifies( - &self, - space: &OwnedRoomId, - room: &OwnedRoomId, - user: &OwnedUserId, - ) -> bool { - let Some(required) = self.room_requirements.get(space).and_then(|r| r.get(room)) else { - return true; - }; - if required.is_empty() { - return true; - } - let Some(assigned) = self.user_roles.get(space).and_then(|u| u.get(user)) else { - return false; - }; - roles_satisfy_requirements(required, assigned) - } - - fn get_power_level(&self, space: &OwnedRoomId, user: &OwnedUserId) -> Option { - let role_defs = self.roles.get(space)?; - let assigned = self.user_roles.get(space)?.get(user)?; - compute_user_power_level(role_defs, assigned) - } -} - -#[test] -fn cache_populate_and_lookup() { - let mut cache = MockCache::new(); - let space = room_id!("!space:example.com").to_owned(); - let child = room_id!("!child:example.com").to_owned(); - let alice = user_id!("@alice:example.com").to_owned(); - - cache.add_space(space.clone(), make_roles(&[("admin", Some(100)), ("nsfw", None)])); - cache.add_child(&space, child.clone()); - cache.assign_role(&space, alice.clone(), "nsfw".to_owned()); - cache.set_room_requirements(&space, child.clone(), make_set(&["nsfw"])); - - assert!(cache.user_qualifies(&space, &child, &alice)); - assert_eq!(cache.get_power_level(&space, &alice), None); -} - -#[test] -fn cache_invalidation_on_role_revoke() { - let mut cache = MockCache::new(); - let space = room_id!("!space:example.com").to_owned(); - let child = room_id!("!nsfw:example.com").to_owned(); - let alice = user_id!("@alice:example.com").to_owned(); - - cache.add_space(space.clone(), make_roles(&[("nsfw", None)])); - cache.assign_role(&space, alice.clone(), "nsfw".to_owned()); - cache.set_room_requirements(&space, child.clone(), make_set(&["nsfw"])); - - assert!(cache.user_qualifies(&space, &child, &alice)); - - cache.revoke_role(&space, &alice, "nsfw"); - assert!(!cache.user_qualifies(&space, &child, &alice)); -} - -#[test] -fn cache_invalidation_on_requirement_change() { - let mut cache = MockCache::new(); - let space = room_id!("!space:example.com").to_owned(); - let child = room_id!("!room:example.com").to_owned(); - let alice = user_id!("@alice:example.com").to_owned(); - - cache.add_space(space.clone(), make_roles(&[("nsfw", None), ("vip", None)])); - cache.assign_role(&space, alice.clone(), "vip".to_owned()); - cache.set_room_requirements(&space, child.clone(), make_set(&["vip"])); - - assert!(cache.user_qualifies(&space, &child, &alice)); - - cache.set_room_requirements(&space, child.clone(), make_set(&["vip", "nsfw"])); - assert!(!cache.user_qualifies(&space, &child, &alice)); -} - -#[test] -fn cache_power_level_updates_on_role_change() { - let mut cache = MockCache::new(); - let space = room_id!("!space:example.com").to_owned(); - let alice = user_id!("@alice:example.com").to_owned(); - - cache.add_space(space.clone(), make_roles(&[("admin", Some(100)), ("mod", Some(50))])); - - assert_eq!(cache.get_power_level(&space, &alice), None); - - cache.assign_role(&space, alice.clone(), "mod".to_owned()); - assert_eq!(cache.get_power_level(&space, &alice), Some(50)); - - cache.assign_role(&space, alice.clone(), "admin".to_owned()); - assert_eq!(cache.get_power_level(&space, &alice), Some(100)); - - cache.revoke_role(&space, &alice, "admin"); - assert_eq!(cache.get_power_level(&space, &alice), Some(50)); -} diff --git a/src/service/rooms/roles/integration_tests.rs b/src/service/rooms/roles/integration_tests.rs deleted file mode 100644 index 344da40b..00000000 --- a/src/service/rooms/roles/integration_tests.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use super::{roles_satisfy_requirements, tests::make_set}; - -#[test] -fn scenario_room_adds_requirement_existing_members_checked() { - let alice_roles = make_set(&["vip"]); - let bob_roles = make_set(&["vip", "nsfw"]); - - let empty_reqs: HashSet = HashSet::new(); - assert!(roles_satisfy_requirements(&empty_reqs, &alice_roles)); - assert!(roles_satisfy_requirements(&empty_reqs, &bob_roles)); - - let new_reqs = make_set(&["nsfw"]); - assert!(!roles_satisfy_requirements(&new_reqs, &alice_roles)); - assert!(roles_satisfy_requirements(&new_reqs, &bob_roles)); -} - -#[test] -fn scenario_multiple_rooms_different_requirements() { - let alice_roles = make_set(&["nsfw", "vip"]); - let bob_roles = make_set(&["nsfw"]); - - let nsfw_reqs = make_set(&["nsfw"]); - let vip_reqs = make_set(&["vip"]); - let both_reqs = make_set(&["nsfw", "vip"]); - - assert!(roles_satisfy_requirements(&nsfw_reqs, &alice_roles)); - assert!(roles_satisfy_requirements(&vip_reqs, &alice_roles)); - assert!(roles_satisfy_requirements(&both_reqs, &alice_roles)); - - assert!(roles_satisfy_requirements(&nsfw_reqs, &bob_roles)); - assert!(!roles_satisfy_requirements(&vip_reqs, &bob_roles)); - assert!(!roles_satisfy_requirements(&both_reqs, &bob_roles)); -} - -#[test] -fn scenario_identify_auto_join_candidates() { - let alice_roles = make_set(&["nsfw", "vip"]); - - let mut room_reqs: HashMap> = HashMap::new(); - room_reqs.insert("general".to_owned(), HashSet::new()); - room_reqs.insert("nsfw-chat".to_owned(), make_set(&["nsfw"])); - room_reqs.insert("vip-lounge".to_owned(), make_set(&["vip"])); - room_reqs.insert("staff-only".to_owned(), make_set(&["staff"])); - - let qualifying: Vec<_> = room_reqs - .iter() - .filter(|(_, reqs)| roles_satisfy_requirements(reqs, &alice_roles)) - .map(|(name, _)| name.clone()) - .collect(); - - assert!(qualifying.contains(&"general".to_owned())); - assert!(qualifying.contains(&"nsfw-chat".to_owned())); - assert!(qualifying.contains(&"vip-lounge".to_owned())); - assert!(!qualifying.contains(&"staff-only".to_owned())); -} - -#[test] -fn scenario_identify_kick_candidates_after_role_revocation() { - let alice_roles_after = make_set(&["vip"]); - - let mut rooms: HashMap> = HashMap::new(); - rooms.insert("general".to_owned(), HashSet::new()); - rooms.insert("nsfw-chat".to_owned(), make_set(&["nsfw"])); - rooms.insert("vip-lounge".to_owned(), make_set(&["vip"])); - rooms.insert("nsfw-vip".to_owned(), make_set(&["nsfw", "vip"])); - - let kick_from: Vec<_> = rooms - .iter() - .filter(|(_, reqs)| !roles_satisfy_requirements(reqs, &alice_roles_after)) - .map(|(name, _)| name.clone()) - .collect(); - - assert!(kick_from.contains(&"nsfw-chat".to_owned())); - assert!(kick_from.contains(&"nsfw-vip".to_owned())); - assert!(!kick_from.contains(&"general".to_owned())); - assert!(!kick_from.contains(&"vip-lounge".to_owned())); -} diff --git a/src/service/rooms/roles/mod.rs b/src/service/rooms/roles/mod.rs index 25459003..676d5160 100644 --- a/src/service/rooms/roles/mod.rs +++ b/src/service/rooms/roles/mod.rs @@ -1,8 +1,4 @@ #[cfg(test)] -mod cache_tests; -#[cfg(test)] -mod integration_tests; -#[cfg(test)] mod tests; use std::{ @@ -239,7 +235,8 @@ pub async fn populate_space(&self, space_id: &RoomId) { } if self.roles.read().await.len() - >= usize::try_from(self.server.config.space_roles_cache_capacity).unwrap_or(usize::MAX) + >= usize::try_from(self.server.config.space_roles_cache_flush_threshold) + .unwrap_or(usize::MAX) { self.roles.write().await.clear(); self.user_roles.write().await.clear(); @@ -607,47 +604,55 @@ pub async fn auto_join_qualifying_rooms(&self, space_id: &RoomId, user_id: &User continue; } - let state_lock = self.services.state.mutex.lock(child_room_id).await; - if let Err(e) = self - .services - .timeline - .build_and_append_pdu( - PduBuilder::state( - user_id.to_string(), - &RoomMemberEventContent::new(MembershipState::Invite), - ), - server_user, - Some(child_room_id), - &state_lock, - ) + .invite_and_join_user(child_room_id, user_id, server_user) .await { - debug_warn!(user_id = %user_id, room_id = %child_room_id, error = ?e, "Failed to invite user during auto-join"); - continue; - } - - if let Err(e) = self - .services - .timeline - .build_and_append_pdu( - PduBuilder::state( - user_id.to_string(), - &RoomMemberEventContent::new(MembershipState::Join), - ), - user_id, - Some(child_room_id), - &state_lock, - ) - .await - { - warn!(user_id = %user_id, room_id = %child_room_id, error = ?e, "Failed to auto-join user"); + debug_warn!(user_id = %user_id, room_id = %child_room_id, error = ?e, "Failed to auto-join user"); } } Ok(()) } +#[implement(Service)] +async fn invite_and_join_user( + &self, + room_id: &RoomId, + user_id: &UserId, + server_user: &UserId, +) -> Result { + let state_lock = self.services.state.mutex.lock(room_id).await; + + self.services + .timeline + .build_and_append_pdu( + PduBuilder::state( + user_id.to_string(), + &RoomMemberEventContent::new(MembershipState::Invite), + ), + server_user, + Some(room_id), + &state_lock, + ) + .await?; + + self.services + .timeline + .build_and_append_pdu( + PduBuilder::state( + user_id.to_string(), + &RoomMemberEventContent::new(MembershipState::Join), + ), + user_id, + Some(room_id), + &state_lock, + ) + .await?; + + Ok(()) +} + impl Service { pub fn handle_state_event_change( self: &Arc, @@ -729,16 +734,11 @@ impl Service { .collect() .await; for member in &members { - if !this - .user_qualifies_for_room(&space_id, target_room, member) - .await + if let Err(e) = + Box::pin(this.kick_unqualified_from_rooms(&space_id, member)) + .await { - if let Err(e) = - Box::pin(this.kick_unqualified_from_rooms(&space_id, member)) - .await - { - debug_warn!(user_id = %member, error = ?e, "Space role requirement kick failed"); - } + debug_warn!(user_id = %member, error = ?e, "Space role requirement kick failed"); } } } @@ -830,55 +830,17 @@ impl Service { for member in &space_members { if this .user_qualifies_for_room(&space_id, &child_room_id, member) + .await && !this + .services + .state_cache + .is_joined(member, &child_room_id) .await { - if !this - .services - .state_cache - .is_joined(member, &child_room_id) + if let Err(e) = this + .invite_and_join_user(&child_room_id, member, server_user) .await { - let state_lock = - this.services.state.mutex.lock(&child_room_id).await; - - if let Err(e) = this - .services - .timeline - .build_and_append_pdu( - PduBuilder::state( - member.to_string(), - &RoomMemberEventContent::new( - MembershipState::Invite, - ), - ), - server_user, - Some(&child_room_id), - &state_lock, - ) - .await - { - debug_warn!(user_id = %member, room_id = %child_room_id, error = ?e, "Failed to invite user"); - continue; - } - - if let Err(e) = this - .services - .timeline - .build_and_append_pdu( - PduBuilder::state( - member.to_string(), - &RoomMemberEventContent::new( - MembershipState::Join, - ), - ), - member, - Some(&child_room_id), - &state_lock, - ) - .await - { - warn!(user_id = %member, room_id = %child_room_id, error = ?e, "Failed to auto-join user"); - } + debug_warn!(user_id = %member, room_id = %child_room_id, error = ?e, "Failed to auto-join user"); } } } diff --git a/src/service/rooms/timeline/append.rs b/src/service/rooms/timeline/append.rs index ef537284..b3ddcf94 100644 --- a/src/service/rooms/timeline/append.rs +++ b/src/service/rooms/timeline/append.rs @@ -363,6 +363,7 @@ where | _ => {}, } + let roles: Arc = Arc::clone(&*self.services.roles); if let Some(state_key) = pdu.state_key() { let event_type_str = pdu.event_type().to_string(); match event_type_str.as_str() { @@ -374,8 +375,6 @@ where self.services.state_accessor.get_room_type(room_id).await, Ok(ruma::room::RoomType::Space) ) { - let roles: Arc = - Arc::clone(&*self.services.roles); roles.handle_state_event_change( room_id.to_owned(), event_type_str, @@ -386,10 +385,9 @@ where | _ => {}, } } - if *pdu.kind() == TimelineEventType::SpaceChild { - if let Some(state_key) = pdu.state_key() { + if let Some(state_key) = pdu.state_key() { + if *pdu.kind() == TimelineEventType::SpaceChild { if let Ok(child_room_id) = ruma::RoomId::parse(state_key) { - let roles: Arc = Arc::clone(&*self.services.roles); roles.handle_space_child_change(room_id.to_owned(), child_room_id.to_owned()); } } @@ -404,7 +402,6 @@ where self.services.state_accessor.get_room_type(room_id).await, Ok(ruma::room::RoomType::Space) ) { - let roles: Arc = Arc::clone(&*self.services.roles); roles.handle_space_member_join(room_id.to_owned(), user_id.to_owned()); } diff --git a/src/service/rooms/timeline/build.rs b/src/service/rooms/timeline/build.rs index 9dfe1c69..b0a47076 100644 --- a/src/service/rooms/timeline/build.rs +++ b/src/service/rooms/timeline/build.rs @@ -110,7 +110,9 @@ pub async fn build_and_append_pdu( use ruma::events::room::power_levels::RoomPowerLevelsEventContent; let parent_spaces = self.services.roles.get_parent_spaces(&room_id).await; - if let Ok(proposed) = pdu.get_content::() { + if !parent_spaces.is_empty() + && let Ok(proposed) = pdu.get_content::() + { let space_data: Vec = { let user_roles_guard = self.services.roles.user_roles.read().await; let roles_guard = self.services.roles.roles.read().await;