diff --git a/changelog.d/+space-permission-cascading.feature.md b/changelog.d/+space-permission-cascading.feature.md new file mode 100644 index 00000000..4f25977d --- /dev/null +++ b/changelog.d/+space-permission-cascading.feature.md @@ -0,0 +1 @@ +Add Space permission cascading: power levels cascade from Spaces to child rooms, role-based room access with custom roles, continuous enforcement (auto-join/kick), and admin commands for role management. Server-wide default controlled by `space_permission_cascading` config flag (off by default), with per-Space overrides via `!admin space roles enable/disable `. diff --git a/src/api/client/membership/join.rs b/src/api/client/membership/join.rs index cbb82506..a175f1b6 100644 --- a/src/api/client/membership/join.rs +++ b/src/api/client/membership/join.rs @@ -347,6 +347,12 @@ pub async fn join_room_by_id_helper( } } + services + .rooms + .roles + .check_join_allowed(room_id, sender_user) + .await?; + if server_in_room { join_room_by_id_helper_local(services, sender_user, room_id, reason, servers, state_lock) .boxed() diff --git a/src/service/rooms/timeline/append.rs b/src/service/rooms/timeline/append.rs index 40139a98..6a8cc257 100644 --- a/src/service/rooms/timeline/append.rs +++ b/src/service/rooms/timeline/append.rs @@ -327,7 +327,7 @@ where } }, | TimelineEventType::SpaceChild => - if let Some(_state_key) = pdu.state_key() { + if pdu.state_key().is_some() { self.services .spaces .roomid_spacehierarchy_cache @@ -359,6 +359,8 @@ where | _ => {}, } + self.services.roles.on_pdu_appended(room_id, &pdu); + // CONCERN: If we receive events with a relation out-of-order, we never write // their relation / thread. We need some kind of way to trigger when we receive // this event, and potentially a way to rebuild the table entirely. diff --git a/src/service/rooms/timeline/build.rs b/src/service/rooms/timeline/build.rs index 51162de9..6df89cdf 100644 --- a/src/service/rooms/timeline/build.rs +++ b/src/service/rooms/timeline/build.rs @@ -97,6 +97,17 @@ pub async fn build_and_append_pdu( ))); } } + if *pdu.kind() == TimelineEventType::RoomPowerLevels { + if let Ok(proposed) = + pdu.get_content::() + { + self.services + .roles + .validate_pl_change(&room_id, pdu.sender(), &proposed) + .await?; + } + } + if *pdu.kind() == TimelineEventType::RoomCreate { trace!("Creating shortroomid for {room_id}"); self.services diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index a35b502c..e97f703e 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -80,6 +80,7 @@ struct Services { threads: Dep, search: Dep, spaces: Dep, + roles: Dep, event_handler: Dep, } @@ -112,6 +113,7 @@ impl crate::Service for Service { threads: args.depend::("rooms::threads"), search: args.depend::("rooms::search"), spaces: args.depend::("rooms::spaces"), + roles: args.depend::("rooms::roles"), event_handler: args .depend::("rooms::event_handler"), },