state-res: Enforce integer PLs for room v10 on custom types

According to MSC3667
This commit is contained in:
Kévin Commaille 2022-06-24 11:57:44 +02:00 committed by Kévin Commaille
parent da462adab1
commit df821ab753
4 changed files with 254 additions and 175 deletions

View File

@ -19,11 +19,14 @@ use serde::{de::IgnoredAny, Deserialize};
use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
use tracing::{debug, error, info, warn};
use crate::{room_version::RoomVersion, Error, Event, PowerLevelsContentFields, Result};
mod int_power_levels;
use int_power_levels::IntRoomPowerLevelsEventContent;
use crate::{
power_levels::{
deserialize_power_levels, deserialize_power_levels_content_fields,
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
},
room_version::RoomVersion,
Error, Event, Result,
};
// FIXME: field extracting could be bundled for `content`
#[derive(Deserialize)]
@ -37,11 +40,6 @@ struct RoomMemberContentFields {
join_authorised_via_users_server: Option<Raw<OwnedUserId>>,
}
#[derive(Deserialize)]
struct PowerLevelsContentInvite {
invite: Int,
}
/// For the given event `kind` what are the relevant auth events that are needed to authenticate
/// this `content`.
///
@ -337,14 +335,11 @@ pub fn auth_check<E: Event>(
// If type is m.room.third_party_invite
let sender_power_level = if let Some(pl) = &power_levels_event {
if let Ok(content) = from_json_str::<PowerLevelsContentFields>(pl.content().get()) {
if let Some(level) = content.users.get(sender) {
*level
} else {
content.users_default
}
let content = deserialize_power_levels_content_fields(pl.content().get(), room_version)?;
if let Some(level) = content.users.get(sender) {
*level
} else {
int!(0) // TODO if this fails DB error?
content.users_default
}
} else {
// If no power level event found the creator gets 100 everyone else gets 0
@ -359,7 +354,8 @@ pub fn auth_check<E: Event>(
if *incoming_event.event_type() == RoomEventType::RoomThirdPartyInvite {
let invite_level = match &power_levels_event {
Some(power_levels) => {
from_json_str::<PowerLevelsContentInvite>(power_levels.content().get())?.invite
deserialize_power_levels_content_invite(power_levels.content().get(), room_version)?
.invite
}
None => int!(0),
};
@ -408,15 +404,12 @@ pub fn auth_check<E: Event>(
if room_version.extra_redaction_checks
&& *incoming_event.event_type() == RoomEventType::RoomRedaction
{
#[derive(Deserialize)]
struct PowerLevelsContentRedact {
redact: Int,
}
let redact_level = power_levels_event
.and_then(|pl| from_json_str::<PowerLevelsContentRedact>(pl.content().get()).ok())
.map(|c| c.redact)
.unwrap_or_else(|| int!(50));
let redact_level = match power_levels_event {
Some(pl) => {
deserialize_power_levels_content_redact(pl.content().get(), room_version)?.redact
}
None => int!(50),
};
if !check_redaction(room_version, incoming_event, sender_power_level, redact_level)? {
return Ok(false);
@ -500,22 +493,18 @@ fn valid_membership_change(
// Is the authorised user allowed to invite users into this room
let (auth_user_pl, invite_level) = if let Some(pl) = &power_levels_event {
// TODO Refactor all powerlevel parsing
let invite = match from_json_str::<PowerLevelsContentInvite>(pl.content().get()) {
Ok(power_levels) => power_levels.invite,
_ => int!(0),
let invite =
deserialize_power_levels_content_invite(pl.content().get(), room_version)?.invite;
let content =
deserialize_power_levels_content_fields(pl.content().get(), room_version)?;
let user_pl = if let Some(level) = content.users.get(user_for_join_auth) {
*level
} else {
content.users_default
};
if let Ok(content) = from_json_str::<PowerLevelsContentFields>(pl.content().get()) {
let user_pl = if let Some(level) = content.users.get(user_for_join_auth) {
*level
} else {
content.users_default
};
(user_pl, invite)
} else {
(int!(0), invite)
}
(user_pl, invite)
} else {
(int!(0), int!(0))
};
@ -877,31 +866,6 @@ 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

@ -1,94 +0,0 @@
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
}
}

View File

@ -1,7 +1,7 @@
use std::{
borrow::Borrow,
cmp::Reverse,
collections::{BTreeMap, BinaryHeap, HashMap, HashSet},
collections::{BinaryHeap, HashMap, HashSet},
hash::Hash,
};
@ -12,14 +12,14 @@ use ruma_common::{
room::member::{MembershipState, RoomMemberEventContent},
RoomEventType, StateEventType,
},
EventId, MilliSecondsSinceUnixEpoch, OwnedUserId, RoomVersionId,
EventId, MilliSecondsSinceUnixEpoch, RoomVersionId,
};
use serde::Deserialize;
use serde_json::from_str as from_json_str;
use tracing::{debug, info, trace, warn};
mod error;
pub mod event_auth;
mod power_levels;
pub mod room_version;
mod state_event;
#[cfg(test)]
@ -27,6 +27,7 @@ mod test_utils;
pub use error::{Error, Result};
pub use event_auth::{auth_check, auth_types_for_event};
use power_levels::PowerLevelsContentFields;
pub use room_version::RoomVersion;
pub use state_event::Event;
@ -335,18 +336,6 @@ where
Ok(sorted)
}
#[derive(Deserialize)]
struct PowerLevelsContentFields {
#[serde(
default,
deserialize_with = "ruma_common::serde::btreemap_deserialize_v1_powerlevel_values"
)]
users: BTreeMap<OwnedUserId, Int>,
#[serde(default, deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel")]
users_default: Int,
}
/// Find the power level for the sender of `event_id` or return a default value of zero.
///
/// Do NOT use this any where but topological sort, we find the power level for the eventId

View File

@ -0,0 +1,220 @@
use std::collections::BTreeMap;
use js_int::Int;
use ruma_common::{
events::{room::power_levels::RoomPowerLevelsEventContent, RoomEventType},
power_levels::{default_power_level, NotificationPowerLevels},
serde::{btreemap_deserialize_v1_powerlevel_values, deserialize_v1_powerlevel},
OwnedUserId,
};
use serde::Deserialize;
use serde_json::{from_str as from_json_str, Error};
use tracing::error;
use crate::RoomVersion;
#[derive(Deserialize)]
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)]
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
}
}
pub(crate) 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
}
}
}
}
#[derive(Deserialize)]
pub(crate) struct PowerLevelsContentFields {
#[serde(default, deserialize_with = "btreemap_deserialize_v1_powerlevel_values")]
pub(crate) users: BTreeMap<OwnedUserId, Int>,
#[serde(default, deserialize_with = "deserialize_v1_powerlevel")]
pub(crate) users_default: Int,
}
#[derive(Deserialize)]
struct IntPowerLevelsContentFields {
#[serde(default)]
users: BTreeMap<OwnedUserId, Int>,
#[serde(default)]
users_default: Int,
}
impl From<IntPowerLevelsContentFields> for PowerLevelsContentFields {
fn from(pl: IntPowerLevelsContentFields) -> Self {
let IntPowerLevelsContentFields { users, users_default } = pl;
Self { users, users_default }
}
}
pub(crate) fn deserialize_power_levels_content_fields(
content: &str,
room_version: &RoomVersion,
) -> Result<PowerLevelsContentFields, Error> {
if room_version.integer_power_levels {
from_json_str::<IntPowerLevelsContentFields>(content).map(|r| r.into())
} else {
from_json_str(content)
}
}
#[derive(Deserialize)]
pub(crate) struct PowerLevelsContentInvite {
#[serde(default, deserialize_with = "deserialize_v1_powerlevel")]
pub(crate) invite: Int,
}
#[derive(Deserialize)]
struct IntPowerLevelsContentInvite {
#[serde(default)]
invite: Int,
}
impl From<IntPowerLevelsContentInvite> for PowerLevelsContentInvite {
fn from(pl: IntPowerLevelsContentInvite) -> Self {
let IntPowerLevelsContentInvite { invite } = pl;
Self { invite }
}
}
pub(crate) fn deserialize_power_levels_content_invite(
content: &str,
room_version: &RoomVersion,
) -> Result<PowerLevelsContentInvite, Error> {
if room_version.integer_power_levels {
from_json_str::<IntPowerLevelsContentInvite>(content).map(|r| r.into())
} else {
from_json_str(content)
}
}
#[derive(Deserialize)]
pub(crate) struct PowerLevelsContentRedact {
#[serde(default = "default_power_level", deserialize_with = "deserialize_v1_powerlevel")]
pub(crate) redact: Int,
}
#[derive(Deserialize)]
pub(crate) struct IntPowerLevelsContentRedact {
#[serde(default = "default_power_level")]
redact: Int,
}
impl From<IntPowerLevelsContentRedact> for PowerLevelsContentRedact {
fn from(pl: IntPowerLevelsContentRedact) -> Self {
let IntPowerLevelsContentRedact { redact } = pl;
Self { redact }
}
}
pub(crate) fn deserialize_power_levels_content_redact(
content: &str,
room_version: &RoomVersion,
) -> Result<PowerLevelsContentRedact, Error> {
if room_version.integer_power_levels {
from_json_str::<IntPowerLevelsContentRedact>(content).map(|r| r.into())
} else {
from_json_str(content)
}
}