State-reset and security mitigations.

Upgrade Ruma to present.

The following are intentionally benign for activation in a later commit:

- Hydra backports not default.
- Room version 12 not default.
- Room version 12 not listed as stable.

Do not enable them manually or you can brick your database.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2025-06-29 03:33:29 +00:00
parent 2c6dd78502
commit 628597c318
134 changed files with 14961 additions and 4935 deletions

View File

@@ -6,6 +6,7 @@ mod user_can;
use std::sync::Arc;
use async_trait::async_trait;
use futures::{FutureExt, TryFutureExt, future::try_join};
use ruma::{
EventEncryptionAlgorithm, JsOption, OwnedRoomAliasId, RoomId, UserId,
events::{
@@ -20,12 +21,16 @@ use ruma::{
join_rules::{JoinRule, RoomJoinRulesEventContent},
member::RoomMemberEventContent,
name::RoomNameEventContent,
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
topic::RoomTopicEventContent,
},
},
room::RoomType,
};
use tuwunel_core::{Result, err};
use tuwunel_core::{
Result, err,
matrix::{Event, room_version, state_res::events::RoomCreateEvent},
};
use tuwunel_database::Map;
use crate::{Dep, rooms};
@@ -69,6 +74,31 @@ impl crate::Service for Service {
}
impl Service {
/// Gets the effective power levels of a room, regardless of if there is an
/// `m.rooms.power_levels` state.
pub async fn get_power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels> {
let create = self.get_create(room_id);
let power_levels = self
.room_state_get_content(room_id, &StateEventType::RoomPowerLevels, "")
.map_ok(|c: RoomPowerLevelsEventContent| c)
.map(Result::ok)
.map(Ok);
let (create, power_levels) = try_join(create, power_levels).await?;
let room_version = create.room_version()?;
let rules = room_version::rules(&room_version)?;
let creators = create.creators(&rules.authorization)?;
Ok(RoomPowerLevels::new(power_levels.into(), &rules.authorization, creators))
}
pub async fn get_create(&self, room_id: &RoomId) -> Result<RoomCreateEvent<impl Event>> {
self.room_state_get(room_id, &StateEventType::RoomCreate, "")
.await
.map(RoomCreateEvent::new)
}
pub async fn get_name(&self, room_id: &RoomId) -> Result<String> {
self.room_state_get_content(room_id, &StateEventType::RoomName, "")
.await

View File

@@ -321,7 +321,9 @@ pub fn state_full(
shortstatehash: ShortStateHash,
) -> impl Stream<Item = ((StateEventType, StateKey), impl Event)> + Send + '_ {
self.state_full_pdus(shortstatehash)
.ready_filter_map(|pdu| Some(((pdu.kind().clone().into(), pdu.state_key()?.into()), pdu)))
.ready_filter_map(|pdu| {
Some(((pdu.kind().to_cow_str().into(), pdu.state_key()?.into()), pdu))
})
}
#[implement(super::Service)]

View File

@@ -5,7 +5,6 @@ use ruma::{
room::{
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
member::{MembershipState, RoomMemberEventContent},
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
},
},
};
@@ -44,28 +43,18 @@ pub async fn user_can_redact(
)));
}
match self
.room_state_get_content::<RoomPowerLevelsEventContent>(
room_id,
&StateEventType::RoomPowerLevels,
"",
)
.await
{
| Ok(pl_event_content) => {
let pl_event: RoomPowerLevels = pl_event_content.into();
Ok(pl_event.user_can_redact_event_of_other(sender)
|| pl_event.user_can_redact_own_event(sender)
&& match redacting_event {
| Ok(redacting_event) =>
if federation {
redacting_event.sender().server_name() == sender.server_name()
} else {
redacting_event.sender() == sender
},
| _ => false,
})
},
match self.get_power_levels(room_id).await {
| Ok(power_levels) => Ok(power_levels.user_can_redact_event_of_other(sender)
|| power_levels.user_can_redact_own_event(sender)
&& match redacting_event {
| Ok(redacting_event) =>
if federation {
redacting_event.sender().server_name() == sender.server_name()
} else {
redacting_event.sender() == sender
},
| _ => false,
}),
| _ => {
// Falling back on m.room.create to judge power level
match self