state-res: Enforce integer power levels for room v10
According to MSC3667
This commit is contained in:
parent
2fcb7315b4
commit
e683d28afe
@ -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 = ¤t_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,
|
||||
|
94
crates/ruma-state-res/src/event_auth/int_power_levels.rs
Normal file
94
crates/ruma-state-res/src/event_auth/int_power_levels.rs
Normal 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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user