state-res: Enforce integer power levels for room v10

According to MSC3667
This commit is contained in:
Kévin Commaille 2022-06-21 16:33:16 +02:00 committed by Kévin Commaille
parent 2fcb7315b4
commit e683d28afe
2 changed files with 137 additions and 10 deletions

View File

@ -21,6 +21,10 @@ use tracing::{debug, error, info, warn};
use crate::{room_version::RoomVersion, Error, Event, PowerLevelsContentFields, Result};
mod int_power_levels;
use int_power_levels::IntRoomPowerLevelsEventContent;
// FIXME: field extracting could be bundled for `content`
#[derive(Deserialize)]
struct GetMembership {
@ -745,22 +749,26 @@ fn check_power_levels(
}
}
// - If any of the keys users_default, events_default, state_default, ban, redact, kick, or
// invite in content are present and not an integer, reject.
// - If either of the keys events or notifications in content are present and not a dictionary
// with values that are integers, reject.
// - If users key in content is not a dictionary with keys that are valid user IDs with values
// that are integers, reject.
let user_content: RoomPowerLevelsEventContent =
deserialize_power_levels(power_event.content().get(), room_version)?;
// Validation of users is done in Ruma, synapse for loops validating user_ids and integers here
info!("validation of power event finished");
let current_state = match previous_power_event {
Some(current_state) => current_state,
// If there is no previous m.room.power_levels event in the room, allow
None => return Some(true),
};
// If users key in content is not a dictionary with keys that are valid user IDs
// with values that are integers (or a string that is an integer), reject.
let user_content =
from_json_str::<RoomPowerLevelsEventContent>(power_event.content().get()).unwrap();
let current_content =
from_json_str::<RoomPowerLevelsEventContent>(current_state.content().get()).unwrap();
// Validation of users is done in Ruma, synapse for loops validating user_ids and integers here
info!("validation of power event finished");
let current_content: RoomPowerLevelsEventContent =
deserialize_power_levels(current_state.content().get(), room_version)?;
let mut user_levels_to_check = BTreeSet::new();
let old_list = &current_content.users;
@ -864,6 +872,31 @@ fn check_power_levels(
Some(true)
}
fn deserialize_power_levels(
content: &str,
room_version: &RoomVersion,
) -> Option<RoomPowerLevelsEventContent> {
if room_version.integer_power_levels {
match from_json_str::<IntRoomPowerLevelsEventContent>(content) {
Ok(content) => Some(content.into()),
Err(_) => {
error!("m.room.power_levels event is not valid with integer values");
None
}
}
} else {
match from_json_str(content) {
Ok(content) => Some(content),
Err(_) => {
error!(
"m.room.power_levels event is not valid with integer or string integer values"
);
None
}
}
}
}
fn get_deserialize_levels(
old: &serde_json::Value,
new: &serde_json::Value,

View File

@ -0,0 +1,94 @@
use std::collections::BTreeMap;
use js_int::Int;
use ruma_common::{
events::{room::power_levels::RoomPowerLevelsEventContent, RoomEventType},
power_levels::{default_power_level, NotificationPowerLevels},
OwnedUserId,
};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct IntRoomPowerLevelsEventContent {
#[serde(default = "default_power_level")]
pub ban: Int,
#[serde(default)]
pub events: BTreeMap<RoomEventType, Int>,
#[serde(default)]
pub events_default: Int,
#[serde(default)]
pub invite: Int,
#[serde(default = "default_power_level")]
pub kick: Int,
#[serde(default = "default_power_level")]
pub redact: Int,
#[serde(default = "default_power_level")]
pub state_default: Int,
#[serde(default)]
pub users: BTreeMap<OwnedUserId, Int>,
#[serde(default)]
pub users_default: Int,
#[serde(default)]
pub notifications: IntNotificationPowerLevels,
}
impl From<IntRoomPowerLevelsEventContent> for RoomPowerLevelsEventContent {
fn from(int_pl: IntRoomPowerLevelsEventContent) -> Self {
let IntRoomPowerLevelsEventContent {
ban,
events,
events_default,
invite,
kick,
redact,
state_default,
users,
users_default,
notifications,
} = int_pl;
let mut pl = Self::new();
pl.ban = ban;
pl.events = events;
pl.events_default = events_default;
pl.invite = invite;
pl.kick = kick;
pl.redact = redact;
pl.state_default = state_default;
pl.users = users;
pl.users_default = users_default;
pl.notifications = notifications.into();
pl
}
}
#[derive(Deserialize)]
pub struct IntNotificationPowerLevels {
#[serde(default = "default_power_level")]
pub room: Int,
}
impl Default for IntNotificationPowerLevels {
fn default() -> Self {
Self { room: default_power_level() }
}
}
impl From<IntNotificationPowerLevels> for NotificationPowerLevels {
fn from(int_notif: IntNotificationPowerLevels) -> Self {
let mut notif = Self::new();
notif.room = int_notif.room;
notif
}
}