feat(spaces): handle space leave/ban, fix enable ordering
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 5s
Checks / Prek / Clippy and Cargo Tests (pull_request) Failing after 6s
Update flake hashes / update-flake-hashes (pull_request) Failing after 5s

Add handle_space_member_leave to kick users from child rooms when they
leave or are banned from a Space. Handle both Join and Leave/Ban
membership transitions in on_pdu_appended dispatch.

Fix enable command to create default roles before sending the cascading
enable event, preventing enforcement from running against empty roles.
This commit is contained in:
ember33 2026-03-19 19:46:40 +01:00
parent 23239b79a9
commit 528c1c501e
2 changed files with 47 additions and 9 deletions

View file

@ -568,15 +568,15 @@ async fn room(&self, space: OwnedRoomOrAliasId, room_id: OwnedRoomId) -> Result
async fn enable(&self, space: OwnedRoomOrAliasId) -> Result {
let space_id = resolve_room_as_space!(self, space);
let content = SpaceCascadingEventContent { enabled: true };
send_space_state!(self, space_id, SPACE_CASCADING_EVENT_TYPE, "", &content);
self.services
.rooms
.roles
.ensure_default_roles(&space_id)
.await?;
let content = SpaceCascadingEventContent { enabled: true };
send_space_state!(self, space_id, SPACE_CASCADING_EVENT_TYPE, "", &content);
self.write_str(&format!("Space permission cascading enabled for {space_id}."))
.await
}

View file

@ -733,12 +733,21 @@ pub fn on_pdu_appended(self: &Arc<Self>, room_id: &RoomId, pdu: &PduEvent) {
}
if *pdu.kind() == ruma::events::TimelineEventType::RoomMember {
if let Ok(content) = pdu.get_content::<RoomMemberEventContent>() {
if content.membership == MembershipState::Join {
if let Ok(user_id) = UserId::parse(&*state_key) {
match content.membership {
| MembershipState::Join => {
self.handle_space_member_join(
room_id.to_owned(),
user_id.to_owned(),
);
},
| MembershipState::Leave | MembershipState::Ban => {
self.handle_space_member_leave(
room_id.to_owned(),
user_id.to_owned(),
);
},
| _ => {},
}
}
}
@ -1089,6 +1098,35 @@ impl Service {
this.sync_power_levels_for_children(&space_id).await;
});
}
pub fn handle_space_member_leave(
self: &Arc<Self>,
space_id: OwnedRoomId,
user_id: OwnedUserId,
) {
if user_id == self.services.globals.server_user {
return;
}
let this = Arc::clone(self);
self.server.runtime().spawn(async move {
if !this.server.running() {
return;
}
if !this.is_enabled_for_space(&space_id).await {
return;
}
let Ok(_permit) = this.enforcement_semaphore.acquire().await else {
return;
};
if let Err(e) = Box::pin(this.kick_unqualified_from_rooms(&space_id, &user_id)).await
{
debug_warn!(user_id = %user_id, error = ?e, "Kick on Space leave failed");
}
});
}
}
#[implement(Service)]