events / state-res: Use RawJsonValue over JsonValue for event content
This commit is contained in:
parent
1df1256a86
commit
a9985d7763
@ -4,6 +4,7 @@ Breaking changes:
|
||||
|
||||
* Remove `RedactedStrippedStateEvent`
|
||||
* It was not used anywhere since stripped state events are never actually redacted.
|
||||
* Use `Box<RawJsonValue>` instead of `JsonValue` for PDU `content` field
|
||||
|
||||
# 0.24.5
|
||||
|
||||
|
@ -10,13 +10,16 @@ use std::collections::BTreeMap;
|
||||
use js_int::UInt;
|
||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
||||
use ruma_identifiers::{EventId, RoomId, ServerNameBox, ServerSigningKeyId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use serde::{
|
||||
de::{Error as _, IgnoredAny},
|
||||
Deserialize, Deserializer, Serialize,
|
||||
};
|
||||
use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
|
||||
|
||||
use crate::EventType;
|
||||
|
||||
/// Enum for PDU schemas
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(untagged)]
|
||||
pub enum Pdu {
|
||||
@ -55,7 +58,7 @@ pub struct RoomV1Pdu {
|
||||
pub kind: EventType,
|
||||
|
||||
/// The event's content.
|
||||
pub content: JsonValue,
|
||||
pub content: Box<RawJsonValue>,
|
||||
|
||||
/// A key that determines which piece of room state the event represents.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@ -81,7 +84,7 @@ pub struct RoomV1Pdu {
|
||||
/// Additional data added by the origin server but not covered by the
|
||||
/// signatures.
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub unsigned: BTreeMap<String, JsonValue>,
|
||||
pub unsigned: BTreeMap<String, Box<RawJsonValue>>,
|
||||
|
||||
/// Content hashes of the PDU.
|
||||
pub hashes: EventHash,
|
||||
@ -115,7 +118,7 @@ pub struct RoomV3Pdu {
|
||||
pub kind: EventType,
|
||||
|
||||
/// The event's content.
|
||||
pub content: JsonValue,
|
||||
pub content: Box<RawJsonValue>,
|
||||
|
||||
/// A key that determines which piece of room state the event represents.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@ -139,7 +142,7 @@ pub struct RoomV3Pdu {
|
||||
/// Additional data added by the origin server but not covered by the
|
||||
/// signatures.
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub unsigned: BTreeMap<String, JsonValue>,
|
||||
pub unsigned: BTreeMap<String, Box<RawJsonValue>>,
|
||||
|
||||
/// Content hashes of the PDU.
|
||||
pub hashes: EventHash,
|
||||
@ -162,3 +165,22 @@ impl EventHash {
|
||||
Self { sha256 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Pdu {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct GetEventId {
|
||||
event_id: Option<IgnoredAny>,
|
||||
}
|
||||
|
||||
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
||||
if from_json_str::<GetEventId>(json.get()).map_err(D::Error::custom)?.event_id.is_some() {
|
||||
from_json_str(json.get()).map(Self::RoomV1Pdu).map_err(D::Error::custom)
|
||||
} else {
|
||||
from_json_str(json.get()).map(Self::RoomV3Pdu).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ use ruma_events::{
|
||||
EventType,
|
||||
};
|
||||
use ruma_identifiers::{event_id, room_id, server_name, server_signing_key_id, user_id};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
use serde_json::{
|
||||
from_value as from_json_value, json, to_value as to_json_value,
|
||||
value::to_raw_value as to_raw_json_value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn serialize_pdu_as_v1() {
|
||||
@ -21,7 +24,7 @@ fn serialize_pdu_as_v1() {
|
||||
signatures.insert(server_name!("example.com"), inner_signature);
|
||||
|
||||
let mut unsigned = BTreeMap::new();
|
||||
unsigned.insert("somekey".into(), json!({"a": 456}));
|
||||
unsigned.insert("somekey".into(), to_raw_json_value(&json!({ "a": 456 })).unwrap());
|
||||
|
||||
let v1_pdu = RoomV1Pdu {
|
||||
room_id: room_id!("!n8f893n9:example.com"),
|
||||
@ -30,7 +33,7 @@ fn serialize_pdu_as_v1() {
|
||||
origin: "matrix.org".into(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch(1_592_050_773_658_u64.try_into().unwrap()),
|
||||
kind: EventType::RoomPowerLevels,
|
||||
content: json!({"testing": 123}),
|
||||
content: to_raw_json_value(&json!({ "testing": 123 })).unwrap(),
|
||||
state_key: Some("state".into()),
|
||||
prev_events: vec![(
|
||||
event_id!("$previousevent:matrix.org"),
|
||||
@ -88,7 +91,7 @@ fn serialize_pdu_as_v3() {
|
||||
signatures.insert(server_name!("example.com"), inner_signature);
|
||||
|
||||
let mut unsigned = BTreeMap::new();
|
||||
unsigned.insert("somekey".into(), json!({ "a": 456 }));
|
||||
unsigned.insert("somekey".into(), to_raw_json_value(&json!({ "a": 456 })).unwrap());
|
||||
|
||||
let v3_pdu = RoomV3Pdu {
|
||||
room_id: room_id!("!n8f893n9:example.com"),
|
||||
@ -96,7 +99,7 @@ fn serialize_pdu_as_v3() {
|
||||
origin: "matrix.org".into(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch(1_592_050_773_658_u64.try_into().unwrap()),
|
||||
kind: EventType::RoomPowerLevels,
|
||||
content: json!({"testing": 123}),
|
||||
content: to_raw_json_value(&json!({ "testing": 123 })).unwrap(),
|
||||
state_key: Some("state".into()),
|
||||
prev_events: vec![event_id!("$previousevent:matrix.org")],
|
||||
depth: 2_u32.into(),
|
||||
@ -205,7 +208,6 @@ fn deserialize_pdu_as_v3() {
|
||||
"key": "value"
|
||||
},
|
||||
"depth": 12,
|
||||
"event_id": "$a4ecee13e2accdadf56c1025:example.com",
|
||||
"hashes": {
|
||||
"sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted"
|
||||
},
|
||||
|
@ -1,5 +1,10 @@
|
||||
# [unreleased]
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* Remove some trait methods from `Event`
|
||||
* Update `Event::content` signature to return `&RawJsonValue` instead of `&JsonValue`
|
||||
|
||||
# 0.4.1
|
||||
|
||||
Improvements:
|
||||
|
@ -15,6 +15,7 @@ edition = "2018"
|
||||
all-features = true
|
||||
|
||||
[features]
|
||||
compat = []
|
||||
unstable-pre-spec = ["ruma-events/unstable-pre-spec"]
|
||||
unstable-exhaustive-types = []
|
||||
|
||||
|
@ -18,7 +18,7 @@ use std::{
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use event::StateEvent;
|
||||
use js_int::uint;
|
||||
use js_int::{int, uint};
|
||||
use maplit::{btreemap, hashmap, hashset};
|
||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
||||
use ruma_events::{
|
||||
@ -31,7 +31,10 @@ use ruma_events::{
|
||||
};
|
||||
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
|
||||
use ruma_state_res::{self as state_res, Error, Event, Result, StateMap};
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
use serde_json::{
|
||||
json,
|
||||
value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue},
|
||||
};
|
||||
|
||||
static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
@ -46,7 +49,7 @@ fn lexico_topo_sort(c: &mut Criterion) {
|
||||
};
|
||||
b.iter(|| {
|
||||
let _ = state_res::lexicographical_topological_sort(&graph, |id| {
|
||||
Ok((0, MilliSecondsSinceUnixEpoch(uint!(0)), id.clone()))
|
||||
Ok((int!(0), MilliSecondsSinceUnixEpoch(uint!(0)), id.clone()))
|
||||
});
|
||||
})
|
||||
});
|
||||
@ -238,7 +241,7 @@ impl TestStore<StateEvent> {
|
||||
alice(),
|
||||
EventType::RoomCreate,
|
||||
Some(""),
|
||||
json!({ "creator": alice() }),
|
||||
to_raw_json_value(&json!({ "creator": alice() })).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
@ -261,7 +264,7 @@ impl TestStore<StateEvent> {
|
||||
alice(),
|
||||
EventType::RoomJoinRules,
|
||||
Some(""),
|
||||
json!({ "join_rule": JoinRule::Public }),
|
||||
to_raw_json_value(&json!({ "join_rule": JoinRule::Public })).unwrap(),
|
||||
&[cre.clone(), alice_mem.event_id().clone()],
|
||||
&[alice_mem.event_id().clone()],
|
||||
);
|
||||
@ -335,12 +338,15 @@ fn event_id(id: &str) -> EventId {
|
||||
fn alice() -> UserId {
|
||||
UserId::try_from("@alice:foo").unwrap()
|
||||
}
|
||||
|
||||
fn bob() -> UserId {
|
||||
UserId::try_from("@bob:foo").unwrap()
|
||||
}
|
||||
|
||||
fn charlie() -> UserId {
|
||||
UserId::try_from("@charlie:foo").unwrap()
|
||||
}
|
||||
|
||||
fn ella() -> UserId {
|
||||
UserId::try_from("@ella:foo").unwrap()
|
||||
}
|
||||
@ -349,12 +355,12 @@ fn room_id() -> RoomId {
|
||||
RoomId::try_from("!test:foo").unwrap()
|
||||
}
|
||||
|
||||
fn member_content_ban() -> JsonValue {
|
||||
serde_json::to_value(MemberEventContent::new(MembershipState::Ban)).unwrap()
|
||||
fn member_content_ban() -> Box<RawJsonValue> {
|
||||
to_raw_json_value(&MemberEventContent::new(MembershipState::Ban)).unwrap()
|
||||
}
|
||||
|
||||
fn member_content_join() -> JsonValue {
|
||||
serde_json::to_value(MemberEventContent::new(MembershipState::Join)).unwrap()
|
||||
fn member_content_join() -> Box<RawJsonValue> {
|
||||
to_raw_json_value(&MemberEventContent::new(MembershipState::Join)).unwrap()
|
||||
}
|
||||
|
||||
fn to_pdu_event<S>(
|
||||
@ -362,7 +368,7 @@ fn to_pdu_event<S>(
|
||||
sender: UserId,
|
||||
ev_type: EventType,
|
||||
state_key: Option<&str>,
|
||||
content: JsonValue,
|
||||
content: Box<RawJsonValue>,
|
||||
auth_events: &[S],
|
||||
prev_events: &[S],
|
||||
) -> Arc<StateEvent>
|
||||
@ -408,7 +414,7 @@ fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomCreate,
|
||||
Some(""),
|
||||
json!({ "creator": alice() }),
|
||||
to_raw_json_value(&json!({ "creator": alice() })).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
),
|
||||
@ -426,7 +432,7 @@ fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice().to_string(): 100 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice().to_string(): 100 } })).unwrap(),
|
||||
&["CREATE", "IMA"],
|
||||
&["IMA"],
|
||||
),
|
||||
@ -435,7 +441,7 @@ fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomJoinRules,
|
||||
Some(""),
|
||||
json!({ "join_rule": JoinRule::Public }),
|
||||
to_raw_json_value(&json!({ "join_rule": JoinRule::Public })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"],
|
||||
&["IPOWER"],
|
||||
),
|
||||
@ -462,7 +468,7 @@ fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
charlie(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
json!({}),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
),
|
||||
@ -471,7 +477,7 @@ fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
charlie(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
json!({}),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
),
|
||||
@ -490,7 +496,7 @@ fn BAN_STATE_SET() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"], // auth_events
|
||||
&["START"], // prev_events
|
||||
),
|
||||
@ -499,7 +505,7 @@ fn BAN_STATE_SET() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"],
|
||||
&["END"],
|
||||
),
|
||||
@ -533,6 +539,7 @@ mod event {
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
use ruma_state_res::Event;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
impl Event for StateEvent {
|
||||
fn event_id(&self) -> &EventId {
|
||||
@ -566,7 +573,7 @@ mod event {
|
||||
}
|
||||
}
|
||||
|
||||
fn content(&self) -> &serde_json::Value {
|
||||
fn content(&self) -> &RawJsonValue {
|
||||
match &self.rest {
|
||||
Pdu::RoomV1Pdu(ev) => &ev.content,
|
||||
Pdu::RoomV3Pdu(ev) => &ev.content,
|
||||
|
@ -12,20 +12,27 @@ use ruma_events::{
|
||||
EventType,
|
||||
};
|
||||
use ruma_identifiers::{RoomVersionId, UserId};
|
||||
use ruma_serde::Raw;
|
||||
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, Result};
|
||||
use crate::{room_version::RoomVersion, Error, Event, PowerLevelsContentFields, Result};
|
||||
|
||||
/// For the given event `kind` what are the relevant auth events that are needed to authenticate
|
||||
/// this `content`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the supplied `content` is not a JSON object.
|
||||
pub fn auth_types_for_event(
|
||||
kind: &EventType,
|
||||
sender: &UserId,
|
||||
state_key: Option<&str>,
|
||||
content: &serde_json::Value,
|
||||
) -> Vec<(EventType, String)> {
|
||||
content: &RawJsonValue,
|
||||
) -> serde_json::Result<Vec<(EventType, String)>> {
|
||||
if kind == &EventType::RoomCreate {
|
||||
return vec![];
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let mut auth_types = vec![
|
||||
@ -35,11 +42,16 @@ pub fn auth_types_for_event(
|
||||
];
|
||||
|
||||
if kind == &EventType::RoomMember {
|
||||
#[derive(Deserialize)]
|
||||
struct RoomMemberContentFields {
|
||||
membership: Option<Raw<MembershipState>>,
|
||||
third_party_invite: Option<Raw<ThirdPartyInvite>>,
|
||||
}
|
||||
|
||||
if let Some(state_key) = state_key {
|
||||
if let Some(Ok(membership)) = content
|
||||
.get("membership")
|
||||
.map(|m| serde_json::from_value::<MembershipState>(m.clone()))
|
||||
{
|
||||
let content: RoomMemberContentFields = from_json_str(content.get())?;
|
||||
|
||||
if let Some(Ok(membership)) = content.membership.map(|m| m.deserialize()) {
|
||||
if [MembershipState::Join, MembershipState::Invite].contains(&membership) {
|
||||
let key = (EventType::RoomJoinRules, "".to_owned());
|
||||
if !auth_types.contains(&key) {
|
||||
@ -53,10 +65,7 @@ pub fn auth_types_for_event(
|
||||
}
|
||||
|
||||
if membership == MembershipState::Invite {
|
||||
if let Some(Ok(t_id)) = content
|
||||
.get("third_party_invite")
|
||||
.map(|t| serde_json::from_value::<ThirdPartyInvite>(t.clone()))
|
||||
{
|
||||
if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
|
||||
let key = (EventType::RoomThirdPartyInvite, t_id.signed.token);
|
||||
if !auth_types.contains(&key) {
|
||||
auth_types.push(key);
|
||||
@ -67,7 +76,7 @@ pub fn auth_types_for_event(
|
||||
}
|
||||
}
|
||||
|
||||
auth_types
|
||||
Ok(auth_types)
|
||||
}
|
||||
|
||||
/// Authenticate the incoming `event`.
|
||||
@ -75,15 +84,10 @@ pub fn auth_types_for_event(
|
||||
/// The steps of authentication are:
|
||||
///
|
||||
/// * check that the event is being authenticated for the correct room
|
||||
/// * check that the events signatures are valid
|
||||
/// * then there are checks for specific event types
|
||||
///
|
||||
/// The `fetch_state` closure should gather state from a state snapshot. We need to know if the
|
||||
/// event passes auth against some state not a recursive collection of auth_events fields.
|
||||
///
|
||||
/// ## Returns
|
||||
///
|
||||
/// This returns an `Error` only when serialization fails or some other fatal outcome.
|
||||
pub fn auth_check<E: Event>(
|
||||
room_version: &RoomVersion,
|
||||
incoming_event: impl Event,
|
||||
@ -91,6 +95,11 @@ pub fn auth_check<E: Event>(
|
||||
current_third_party_invite: Option<impl Event>,
|
||||
fetch_state: impl Fn(&EventType, &str) -> Option<E>,
|
||||
) -> Result<bool> {
|
||||
#[derive(Deserialize)]
|
||||
struct RoomMemberContentFields {
|
||||
membership: Option<Raw<MembershipState>>,
|
||||
}
|
||||
|
||||
info!(
|
||||
"auth_check beginning for {} ({})",
|
||||
incoming_event.event_id(),
|
||||
@ -111,6 +120,12 @@ pub fn auth_check<E: Event>(
|
||||
//
|
||||
// 1. If type is m.room.create:
|
||||
if *incoming_event.event_type() == EventType::RoomCreate {
|
||||
#[derive(Deserialize)]
|
||||
struct RoomCreateContentFields {
|
||||
room_version: Option<Raw<RoomVersionId>>,
|
||||
creator: Option<Raw<IgnoredAny>>,
|
||||
}
|
||||
|
||||
info!("start m.room.create check");
|
||||
|
||||
// If it has any previous events, reject
|
||||
@ -125,24 +140,17 @@ pub fn auth_check<E: Event>(
|
||||
return Ok(false); // creation events room id does not match senders
|
||||
}
|
||||
|
||||
let content: RoomCreateContentFields = from_json_str(incoming_event.content().get())?;
|
||||
|
||||
// If content.room_version is present and is not a recognized version, reject
|
||||
if serde_json::from_value::<RoomVersionId>(
|
||||
incoming_event
|
||||
.content()
|
||||
.get("room_version")
|
||||
.cloned()
|
||||
// TODO synapse defaults to version 1
|
||||
.unwrap_or_else(|| serde_json::json!("1")),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
if content.room_version.map(|v| v.deserialize().is_err()).unwrap_or(false) {
|
||||
warn!("invalid room version found in m.room.create event");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// If content has no creator field, reject
|
||||
if incoming_event.content().get("creator").is_none() {
|
||||
warn!("no creator field found in room create content");
|
||||
if content.creator.is_none() {
|
||||
warn!("no creator field found in m.room.create content");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@ -212,12 +220,8 @@ pub fn auth_check<E: Event>(
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
let membership = incoming_event
|
||||
.content()
|
||||
.get("membership")
|
||||
.map(|m| serde_json::from_value::<MembershipState>(m.clone()));
|
||||
|
||||
if !matches!(membership, Some(Ok(_))) {
|
||||
let content: RoomMemberContentFields = from_json_str(incoming_event.content().get())?;
|
||||
if content.membership.and_then(|m| m.deserialize().ok()).is_none() {
|
||||
warn!("no valid membership field found for m.room.member event content");
|
||||
return Ok(false);
|
||||
}
|
||||
@ -244,7 +248,7 @@ pub fn auth_check<E: Event>(
|
||||
}
|
||||
|
||||
// If the sender's current membership state is not join, reject
|
||||
let mem = match sender_member_event {
|
||||
let sender_member_event = match sender_member_event {
|
||||
Some(mem) => mem,
|
||||
None => {
|
||||
warn!("sender not found in room");
|
||||
@ -252,12 +256,12 @@ pub fn auth_check<E: Event>(
|
||||
}
|
||||
};
|
||||
|
||||
let membership_state = serde_json::from_value::<MembershipState>(
|
||||
mem.content()
|
||||
.get("membership")
|
||||
.expect("we should test before that this field exists")
|
||||
.clone(),
|
||||
)?;
|
||||
let sender_membership_event_content: RoomMemberContentFields =
|
||||
from_json_str(sender_member_event.content().get())?;
|
||||
let membership_state = sender_membership_event_content
|
||||
.membership
|
||||
.expect("we should test before that this field exists")
|
||||
.deserialize()?;
|
||||
|
||||
if !matches!(membership_state, MembershipState::Join) {
|
||||
warn!("sender's membership is not join");
|
||||
@ -265,9 +269,7 @@ pub fn auth_check<E: Event>(
|
||||
}
|
||||
|
||||
let sender_power_level = if let Some(pl) = &power_levels_event {
|
||||
if let Ok(content) =
|
||||
serde_json::from_value::<PowerLevelsEventContent>(pl.content().to_owned())
|
||||
{
|
||||
if let Ok(content) = from_json_str::<PowerLevelsContentFields>(pl.content().get()) {
|
||||
if let Some(level) = content.users.get(sender) {
|
||||
*level
|
||||
} else {
|
||||
@ -279,9 +281,7 @@ pub fn auth_check<E: Event>(
|
||||
} else {
|
||||
// If no power level event found the creator gets 100 everyone else gets 0
|
||||
room_create_event
|
||||
.and_then(|create| {
|
||||
serde_json::from_value::<CreateEventContent>(create.content().to_owned()).ok()
|
||||
})
|
||||
.and_then(|create| from_json_str::<CreateEventContent>(create.content().get()).ok())
|
||||
.and_then(|create| (create.creator == *sender).then(|| int!(100)))
|
||||
.unwrap_or_default()
|
||||
};
|
||||
@ -289,12 +289,14 @@ pub fn auth_check<E: Event>(
|
||||
// Allow if and only if sender's current power level is greater than
|
||||
// or equal to the invite level
|
||||
if *incoming_event.event_type() == EventType::RoomThirdPartyInvite {
|
||||
#[derive(Deserialize)]
|
||||
struct PowerLevelsContentInvite {
|
||||
invite: Int,
|
||||
}
|
||||
|
||||
let invite_level = match &power_levels_event {
|
||||
Some(power_levels) => {
|
||||
serde_json::from_value::<PowerLevelsEventContent>(
|
||||
power_levels.content().to_owned(),
|
||||
)?
|
||||
.invite
|
||||
from_json_str::<PowerLevelsContentInvite>(power_levels.content().get())?.invite
|
||||
}
|
||||
None => int!(50),
|
||||
};
|
||||
@ -342,17 +344,15 @@ pub fn auth_check<E: Event>(
|
||||
if room_version.extra_redaction_checks
|
||||
&& *incoming_event.event_type() == EventType::RoomRedaction
|
||||
{
|
||||
let default = int!(50);
|
||||
let redact_level = if let Some(pl) = power_levels_event {
|
||||
// TODO do this the right way and deserialize
|
||||
if let Some(level) = pl.content().get("redact") {
|
||||
level.to_string().parse().unwrap_or(default)
|
||||
} else {
|
||||
int!(0)
|
||||
}
|
||||
} else {
|
||||
default
|
||||
};
|
||||
#[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));
|
||||
|
||||
if !check_redaction(room_version, incoming_event, sender_power_level, redact_level)? {
|
||||
return Ok(false);
|
||||
@ -379,28 +379,30 @@ fn valid_membership_change(
|
||||
target_user_membership_event: Option<impl Event>,
|
||||
sender: &UserId,
|
||||
sender_membership_event: Option<impl Event>,
|
||||
content: &serde_json::Value,
|
||||
content: &RawJsonValue,
|
||||
prev_event: Option<impl Event>,
|
||||
current_third_party_invite: Option<impl Event>,
|
||||
power_levels_event: Option<impl Event>,
|
||||
join_rules_event: Option<impl Event>,
|
||||
) -> Result<bool> {
|
||||
let target_membership = serde_json::from_value::<MembershipState>(
|
||||
content.get("membership").expect("we test before that this field exists").clone(),
|
||||
)?;
|
||||
// FIXME: field extracting could be bundled for `content`
|
||||
#[derive(Deserialize)]
|
||||
struct GetMembership {
|
||||
membership: MembershipState,
|
||||
}
|
||||
|
||||
let third_party_invite = content
|
||||
.get("third_party_invite")
|
||||
.map(|t| serde_json::from_value::<ThirdPartyInvite>(t.clone()));
|
||||
#[derive(Deserialize)]
|
||||
struct GetThirdPartyInvite {
|
||||
third_party_invite: Option<Raw<ThirdPartyInvite>>,
|
||||
}
|
||||
|
||||
let target_membership = from_json_str::<GetMembership>(content.get())?.membership;
|
||||
let third_party_invite =
|
||||
from_json_str::<GetThirdPartyInvite>(content.get())?.third_party_invite;
|
||||
|
||||
let sender_is_joined = {
|
||||
let sender_membership = match &sender_membership_event {
|
||||
Some(pdu) => serde_json::from_value(
|
||||
pdu.content()
|
||||
.get("membership")
|
||||
.expect("we assume existing events are valid")
|
||||
.clone(),
|
||||
)?,
|
||||
Some(pdu) => from_json_str::<GetMembership>(pdu.content().get())?.membership,
|
||||
None => MembershipState::Leave,
|
||||
};
|
||||
|
||||
@ -408,14 +410,12 @@ fn valid_membership_change(
|
||||
};
|
||||
|
||||
let target_user_current_membership = match &target_user_membership_event {
|
||||
Some(pdu) => serde_json::from_value(
|
||||
pdu.content().get("membership").expect("we assume existing events are valid").clone(),
|
||||
)?,
|
||||
Some(pdu) => from_json_str::<GetMembership>(pdu.content().get())?.membership,
|
||||
None => MembershipState::Leave,
|
||||
};
|
||||
|
||||
let power_levels: PowerLevelsEventContent = match &power_levels_event {
|
||||
Some(ev) => serde_json::from_value(ev.content().to_owned())?,
|
||||
Some(ev) => from_json_str(ev.content().get())?,
|
||||
None => PowerLevelsEventContent::default(),
|
||||
};
|
||||
|
||||
@ -430,8 +430,7 @@ fn valid_membership_change(
|
||||
|
||||
let mut join_rules = JoinRule::Invite;
|
||||
if let Some(jr) = &join_rules_event {
|
||||
join_rules =
|
||||
serde_json::from_value::<JoinRulesEventContent>(jr.content().to_owned())?.join_rule;
|
||||
join_rules = from_json_str::<JoinRulesEventContent>(jr.content().get())?.join_rule;
|
||||
}
|
||||
|
||||
if let Some(prev) = prev_event {
|
||||
@ -471,7 +470,7 @@ fn valid_membership_change(
|
||||
}
|
||||
MembershipState::Invite => {
|
||||
// If content has third_party_invite key
|
||||
if let Some(Ok(tp_id)) = third_party_invite {
|
||||
if let Some(tp_id) = third_party_invite.and_then(|i| i.deserialize().ok()) {
|
||||
if target_user_current_membership == MembershipState::Ban {
|
||||
warn!(?target_user_membership_event_id, "Can't invite banned user");
|
||||
false
|
||||
@ -614,12 +613,10 @@ fn check_power_levels(
|
||||
// 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 =
|
||||
serde_json::from_value::<PowerLevelsEventContent>(power_event.content().to_owned())
|
||||
.unwrap();
|
||||
from_json_str::<PowerLevelsEventContent>(power_event.content().get()).unwrap();
|
||||
|
||||
let current_content =
|
||||
serde_json::from_value::<PowerLevelsEventContent>(current_state.content().to_owned())
|
||||
.unwrap();
|
||||
from_json_str::<PowerLevelsEventContent>(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");
|
||||
@ -771,7 +768,7 @@ fn get_send_level(
|
||||
) -> Int {
|
||||
power_lvl
|
||||
.and_then(|ple| {
|
||||
serde_json::from_value::<PowerLevelsEventContent>(ple.content().to_owned())
|
||||
from_json_str::<PowerLevelsEventContent>(ple.content().get())
|
||||
.map(|content| {
|
||||
content.events.get(e_type).copied().unwrap_or_else(|| {
|
||||
if state_key.is_some() {
|
||||
@ -813,9 +810,9 @@ fn verify_third_party_invite(
|
||||
|
||||
// If any signature in signed matches any public key in the m.room.third_party_invite event,
|
||||
// allow
|
||||
if let Ok(tpid_ev) = serde_json::from_value::<ThirdPartyInviteEventContent>(
|
||||
current_tpid.content().to_owned(),
|
||||
) {
|
||||
if let Ok(tpid_ev) =
|
||||
from_json_str::<ThirdPartyInviteEventContent>(current_tpid.content().get())
|
||||
{
|
||||
// A list of public keys in the public_keys field
|
||||
for key in tpid_ev.public_keys.unwrap_or_default() {
|
||||
if key.public_key == tp_id.signed.token {
|
||||
|
@ -1,18 +1,18 @@
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
collections::{BinaryHeap, HashMap, HashSet},
|
||||
collections::{BTreeMap, BinaryHeap, HashMap, HashSet},
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use js_int::{int, Int};
|
||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
||||
use ruma_events::{
|
||||
room::{
|
||||
member::{MemberEventContent, MembershipState},
|
||||
power_levels::PowerLevelsEventContent,
|
||||
},
|
||||
room::member::{MemberEventContent, MembershipState},
|
||||
EventType,
|
||||
};
|
||||
use ruma_identifiers::{EventId, RoomVersionId};
|
||||
use ruma_identifiers::{EventId, RoomVersionId, UserId};
|
||||
use serde::Deserialize;
|
||||
use serde_json::from_str as from_json_str;
|
||||
use tracing::{debug, info, trace, warn};
|
||||
|
||||
mod error;
|
||||
@ -221,7 +221,7 @@ fn reverse_topological_power_sort<E: Event>(
|
||||
// This is used in the `key_fn` passed to the lexico_topo_sort fn
|
||||
let mut event_to_pl = HashMap::new();
|
||||
for event_id in graph.keys() {
|
||||
let pl = get_power_level_for_sender(event_id, &fetch_event);
|
||||
let pl = get_power_level_for_sender(event_id, &fetch_event)?;
|
||||
info!("{} power level {}", event_id, pl);
|
||||
|
||||
event_to_pl.insert(event_id.clone(), pl);
|
||||
@ -253,7 +253,7 @@ pub fn lexicographical_topological_sort<F>(
|
||||
key_fn: F,
|
||||
) -> Result<Vec<EventId>>
|
||||
where
|
||||
F: Fn(&EventId) -> Result<(i64, MilliSecondsSinceUnixEpoch, EventId)>,
|
||||
F: Fn(&EventId) -> Result<(Int, MilliSecondsSinceUnixEpoch, EventId)>,
|
||||
{
|
||||
info!("starting lexicographical topological sort");
|
||||
// NOTE: an event that has no incoming edges happened most recently,
|
||||
@ -314,11 +314,25 @@ where
|
||||
Ok(sorted)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PowerLevelsContentFields {
|
||||
#[cfg_attr(
|
||||
feature = "compat",
|
||||
serde(deserialize_with = "ruma_serde::btreemap_int_or_string_to_int_values")
|
||||
)]
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
users: BTreeMap<UserId, Int>,
|
||||
|
||||
#[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))]
|
||||
#[serde(default, skip_serializing_if = "ruma_serde::is_default")]
|
||||
users_default: Int,
|
||||
}
|
||||
|
||||
/// Find the power level for the sender of `event_id` or return a default value of zero.
|
||||
fn get_power_level_for_sender<E: Event>(
|
||||
event_id: &EventId,
|
||||
fetch_event: impl Fn(&EventId) -> Option<E>,
|
||||
) -> i64 {
|
||||
) -> serde_json::Result<Int> {
|
||||
info!("fetch event ({}) senders power level", event_id);
|
||||
|
||||
let event = fetch_event(event_id);
|
||||
@ -333,23 +347,19 @@ fn get_power_level_for_sender<E: Event>(
|
||||
}
|
||||
}
|
||||
|
||||
if pl.is_none() {
|
||||
return 0;
|
||||
let content: PowerLevelsContentFields = match pl {
|
||||
None => return Ok(int!(0)),
|
||||
Some(ev) => from_json_str(ev.content().get())?,
|
||||
};
|
||||
|
||||
if let Some(ev) = event {
|
||||
if let Some(&user_level) = content.users.get(ev.sender()) {
|
||||
debug!("found {} at power_level {}", ev.sender(), user_level);
|
||||
return Ok(user_level);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(content) = pl.and_then(|pl| {
|
||||
serde_json::from_value::<PowerLevelsEventContent>(pl.content().to_owned()).ok()
|
||||
}) {
|
||||
if let Some(ev) = event {
|
||||
if let Some(user) = content.users.get(ev.sender()) {
|
||||
debug!("found {} at power_level {}", ev.sender(), user);
|
||||
return (*user).into();
|
||||
}
|
||||
}
|
||||
content.users_default.into()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Ok(content.users_default)
|
||||
}
|
||||
|
||||
/// Check the that each event is authenticated based on the events before it.
|
||||
@ -406,7 +416,7 @@ fn iterative_auth_check<E: Event + Clone>(
|
||||
event.sender(),
|
||||
Some(state_key),
|
||||
event.content(),
|
||||
) {
|
||||
)? {
|
||||
if let Some(ev_id) = resolved_state.get(&key) {
|
||||
if let Some(event) = fetch_event(ev_id) {
|
||||
// TODO synapse checks `rejected_reason` is None here
|
||||
@ -590,9 +600,7 @@ fn is_power_event(event: impl Event) -> bool {
|
||||
event.state_key() == Some("")
|
||||
}
|
||||
EventType::RoomMember => {
|
||||
if let Ok(content) =
|
||||
serde_json::from_value::<MemberEventContent>(event.content().to_owned())
|
||||
{
|
||||
if let Ok(content) = from_json_str::<MemberEventContent>(event.content().get()) {
|
||||
if [MembershipState::Leave, MembershipState::Ban].contains(&content.membership) {
|
||||
return Some(event.sender().as_str()) != event.state_key();
|
||||
}
|
||||
@ -611,13 +619,13 @@ mod tests {
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use js_int::uint;
|
||||
use js_int::{int, uint};
|
||||
use maplit::{hashmap, hashset};
|
||||
use rand::seq::SliceRandom;
|
||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
||||
use ruma_events::{room::join_rules::JoinRule, EventType};
|
||||
use ruma_identifiers::{EventId, RoomVersionId};
|
||||
use serde_json::json;
|
||||
use serde_json::{json, value::to_raw_value as to_raw_json_value};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
@ -710,7 +718,7 @@ mod tests {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"MA",
|
||||
@ -731,7 +739,7 @@ mod tests {
|
||||
bob(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
];
|
||||
|
||||
@ -752,30 +760,48 @@ mod tests {
|
||||
tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish());
|
||||
|
||||
let events = &[
|
||||
to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event(
|
||||
"T1",
|
||||
alice(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PA1",
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"T2",
|
||||
alice(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event(
|
||||
"PA2",
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 0 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 0 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PB",
|
||||
bob(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"T3",
|
||||
bob(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})),
|
||||
];
|
||||
|
||||
let edges =
|
||||
@ -795,15 +821,27 @@ mod tests {
|
||||
tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish());
|
||||
|
||||
let events = &[
|
||||
to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event(
|
||||
"T1",
|
||||
alice(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PA",
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"T2",
|
||||
bob(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event("T2", bob(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event(
|
||||
"MB",
|
||||
alice(),
|
||||
@ -835,7 +873,7 @@ mod tests {
|
||||
alice(),
|
||||
EventType::RoomJoinRules,
|
||||
Some(""),
|
||||
json!({ "join_rule": JoinRule::Private }),
|
||||
to_raw_json_value(&json!({ "join_rule": JoinRule::Private })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"ME",
|
||||
@ -867,21 +905,23 @@ mod tests {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PB",
|
||||
bob(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50, charlie(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50, charlie(): 50 } }))
|
||||
.unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PC",
|
||||
charlie(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50, charlie(): 0 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50, charlie(): 0 } }))
|
||||
.unwrap(),
|
||||
),
|
||||
];
|
||||
|
||||
@ -901,32 +941,62 @@ mod tests {
|
||||
tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish());
|
||||
|
||||
let events = &[
|
||||
to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event(
|
||||
"T1",
|
||||
alice(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PA1",
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"T2",
|
||||
alice(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event(
|
||||
"PA2",
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 0 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 0 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"PB",
|
||||
bob(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"T3",
|
||||
bob(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"MZ1",
|
||||
zara(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event(
|
||||
"T4",
|
||||
alice(),
|
||||
EventType::RoomTopic,
|
||||
Some(""),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
),
|
||||
to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event("MZ1", zara(), EventType::RoomTopic, Some(""), json!({})),
|
||||
to_init_pdu_event("T4", alice(), EventType::RoomTopic, Some(""), json!({})),
|
||||
];
|
||||
|
||||
let edges = vec![
|
||||
@ -986,7 +1056,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let res = crate::lexicographical_topological_sort(&graph, |id| {
|
||||
Ok((0, MilliSecondsSinceUnixEpoch(uint!(0)), id.clone()))
|
||||
Ok((int!(0), MilliSecondsSinceUnixEpoch(uint!(0)), id.clone()))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@ -1120,7 +1190,7 @@ mod tests {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"], // auth_events
|
||||
&["START"], // prev_events
|
||||
),
|
||||
@ -1129,7 +1199,7 @@ mod tests {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice(): 100, bob(): 50 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice(): 100, bob(): 50 } })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"],
|
||||
&["END"],
|
||||
),
|
||||
@ -1165,7 +1235,7 @@ mod tests {
|
||||
alice(),
|
||||
EventType::RoomJoinRules,
|
||||
Some(""),
|
||||
json!({ "join_rule": "invite" }),
|
||||
to_raw_json_value(&json!({ "join_rule": "invite" })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"],
|
||||
&["START"],
|
||||
),
|
||||
|
@ -3,6 +3,7 @@ use std::sync::Arc;
|
||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
||||
use ruma_events::EventType;
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
/// Abstraction of a PDU so users can have their own PDU types.
|
||||
pub trait Event {
|
||||
@ -22,11 +23,7 @@ pub trait Event {
|
||||
fn event_type(&self) -> &EventType;
|
||||
|
||||
/// The event's content.
|
||||
// FIXME: This forces a serde_json::Value to be stored, which the previous solution of returning
|
||||
// an owned one did not. However, the previous signature was even less efficient and also
|
||||
// heavily encouraged storing `serde_json::Value`. We should likely force usage of `RawValue`
|
||||
// instead, or somehow allow different storage without pessimizing all but one.
|
||||
fn content(&self) -> &serde_json::Value;
|
||||
fn content(&self) -> &RawJsonValue;
|
||||
|
||||
/// The state key for this event.
|
||||
fn state_key(&self) -> Option<&str>;
|
||||
@ -64,7 +61,7 @@ impl<T: Event> Event for &T {
|
||||
(*self).event_type()
|
||||
}
|
||||
|
||||
fn content(&self) -> &serde_json::Value {
|
||||
fn content(&self) -> &RawJsonValue {
|
||||
(*self).content()
|
||||
}
|
||||
|
||||
@ -106,7 +103,7 @@ impl<T: Event> Event for Arc<T> {
|
||||
(&**self).event_type()
|
||||
}
|
||||
|
||||
fn content(&self) -> &serde_json::Value {
|
||||
fn content(&self) -> &RawJsonValue {
|
||||
(&**self).content()
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use js_int::uint;
|
||||
use js_int::{int, uint};
|
||||
use ruma_common::MilliSecondsSinceUnixEpoch;
|
||||
use ruma_events::{
|
||||
pdu::{EventHash, Pdu, RoomV3Pdu},
|
||||
@ -18,7 +18,10 @@ use ruma_events::{
|
||||
EventType,
|
||||
};
|
||||
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
use serde_json::{
|
||||
json,
|
||||
value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{auth_types_for_event, Error, Event, Result, StateMap};
|
||||
@ -75,7 +78,7 @@ pub fn do_check(
|
||||
// Resolve the current state and add it to the state_at_event map then continue
|
||||
// on in "time"
|
||||
for node in crate::lexicographical_topological_sort(&graph, |id| {
|
||||
Ok((0, MilliSecondsSinceUnixEpoch(uint!(0)), id.clone()))
|
||||
Ok((int!(0), MilliSecondsSinceUnixEpoch(uint!(0)), id.clone()))
|
||||
})
|
||||
.unwrap()
|
||||
{
|
||||
@ -131,7 +134,8 @@ pub fn do_check(
|
||||
fake_event.sender(),
|
||||
fake_event.state_key(),
|
||||
fake_event.content(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut auth_events = vec![];
|
||||
for key in auth_types {
|
||||
@ -242,7 +246,7 @@ impl TestStore<StateEvent> {
|
||||
alice(),
|
||||
EventType::RoomCreate,
|
||||
Some(""),
|
||||
json!({ "creator": alice() }),
|
||||
to_raw_json_value(&json!({ "creator": alice() })).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
@ -265,7 +269,7 @@ impl TestStore<StateEvent> {
|
||||
alice(),
|
||||
EventType::RoomJoinRules,
|
||||
Some(""),
|
||||
json!({ "join_rule": JoinRule::Public }),
|
||||
to_raw_json_value(&json!({ "join_rule": JoinRule::Public })).unwrap(),
|
||||
&[cre.clone(), alice_mem.event_id().clone()],
|
||||
&[alice_mem.event_id().clone()],
|
||||
);
|
||||
@ -356,12 +360,12 @@ pub fn room_id() -> RoomId {
|
||||
RoomId::try_from("!test:foo").unwrap()
|
||||
}
|
||||
|
||||
pub fn member_content_ban() -> JsonValue {
|
||||
serde_json::to_value(MemberEventContent::new(MembershipState::Ban)).unwrap()
|
||||
pub fn member_content_ban() -> Box<RawJsonValue> {
|
||||
to_raw_json_value(&MemberEventContent::new(MembershipState::Ban)).unwrap()
|
||||
}
|
||||
|
||||
pub fn member_content_join() -> JsonValue {
|
||||
serde_json::to_value(MemberEventContent::new(MembershipState::Join)).unwrap()
|
||||
pub fn member_content_join() -> Box<RawJsonValue> {
|
||||
to_raw_json_value(&MemberEventContent::new(MembershipState::Join)).unwrap()
|
||||
}
|
||||
|
||||
pub fn to_init_pdu_event(
|
||||
@ -369,7 +373,7 @@ pub fn to_init_pdu_event(
|
||||
sender: UserId,
|
||||
ev_type: EventType,
|
||||
state_key: Option<&str>,
|
||||
content: JsonValue,
|
||||
content: Box<RawJsonValue>,
|
||||
) -> Arc<StateEvent> {
|
||||
let ts = SERVER_TIMESTAMP.fetch_add(1, SeqCst);
|
||||
let id = if id.contains('$') { id.to_owned() } else { format!("${}:foo", id) };
|
||||
@ -402,7 +406,7 @@ pub fn to_pdu_event<S>(
|
||||
sender: UserId,
|
||||
ev_type: EventType,
|
||||
state_key: Option<&str>,
|
||||
content: JsonValue,
|
||||
content: Box<RawJsonValue>,
|
||||
auth_events: &[S],
|
||||
prev_events: &[S],
|
||||
) -> Arc<StateEvent>
|
||||
@ -446,7 +450,7 @@ pub fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomCreate,
|
||||
Some(""),
|
||||
json!({ "creator": alice() }),
|
||||
to_raw_json_value(&json!({ "creator": alice() })).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
),
|
||||
@ -464,7 +468,7 @@ pub fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomPowerLevels,
|
||||
Some(""),
|
||||
json!({ "users": { alice().to_string(): 100 } }),
|
||||
to_raw_json_value(&json!({ "users": { alice().to_string(): 100 } })).unwrap(),
|
||||
&["CREATE", "IMA"],
|
||||
&["IMA"],
|
||||
),
|
||||
@ -473,7 +477,7 @@ pub fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
alice(),
|
||||
EventType::RoomJoinRules,
|
||||
Some(""),
|
||||
json!({ "join_rule": JoinRule::Public }),
|
||||
to_raw_json_value(&json!({ "join_rule": JoinRule::Public })).unwrap(),
|
||||
&["CREATE", "IMA", "IPOWER"],
|
||||
&["IPOWER"],
|
||||
),
|
||||
@ -500,7 +504,7 @@ pub fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
charlie(),
|
||||
EventType::RoomMessage,
|
||||
Some("dummy"),
|
||||
json!({}),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
),
|
||||
@ -509,7 +513,7 @@ pub fn INITIAL_EVENTS() -> HashMap<EventId, Arc<StateEvent>> {
|
||||
charlie(),
|
||||
EventType::RoomMessage,
|
||||
Some("dummy"),
|
||||
json!({}),
|
||||
to_raw_json_value(&json!({})).unwrap(),
|
||||
&[],
|
||||
&[],
|
||||
),
|
||||
@ -530,8 +534,8 @@ pub fn INITIAL_EDGES() -> Vec<EventId> {
|
||||
pub mod event {
|
||||
use ruma_events::{exports::ruma_common::MilliSecondsSinceUnixEpoch, pdu::Pdu, EventType};
|
||||
use ruma_identifiers::{EventId, RoomId, UserId};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::Event;
|
||||
|
||||
@ -567,7 +571,7 @@ pub mod event {
|
||||
}
|
||||
}
|
||||
|
||||
fn content(&self) -> &serde_json::Value {
|
||||
fn content(&self) -> &RawJsonValue {
|
||||
match &self.rest {
|
||||
Pdu::RoomV1Pdu(ev) => &ev.content,
|
||||
Pdu::RoomV3Pdu(ev) => &ev.content,
|
||||
|
@ -87,6 +87,7 @@ compat = [
|
||||
"ruma-identifiers/compat",
|
||||
"ruma-client-api/compat",
|
||||
"ruma-signatures/compat",
|
||||
"ruma-state-res/compat",
|
||||
]
|
||||
|
||||
# Helper features that aren't exactly part of the spec but could be helpful
|
||||
|
Loading…
x
Reference in New Issue
Block a user