From 284b797e0513daf56859b64b8c7a506856fb11ec Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 19 Dec 2022 12:10:39 +0100 Subject: [PATCH] Add optional redacted_because insertion to redaction --- crates/ruma-common/src/canonical_json.rs | 52 ++++++++++++++++++- .../ruma-common/src/canonical_json/value.rs | 11 +++- crates/ruma-signatures/src/functions.rs | 6 +-- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/crates/ruma-common/src/canonical_json.rs b/crates/ruma-common/src/canonical_json.rs index 96aba87f..c337b415 100644 --- a/crates/ruma-common/src/canonical_json.rs +++ b/crates/ruma-common/src/canonical_json.rs @@ -8,6 +8,11 @@ use serde_json::Value as JsonValue; mod value; use crate::RoomVersionId; +#[cfg(feature = "events")] +use crate::{ + events::room::redaction::{OriginalRoomRedactionEvent, OriginalSyncRoomRedactionEvent}, + serde::Raw, +}; pub use self::value::{CanonicalJsonObject, CanonicalJsonValue}; @@ -116,6 +121,36 @@ pub fn to_canonical_value( serde_json::to_value(value).map_err(CanonicalJsonError::SerDe)?.try_into() } +/// The value to put in `unsigned.redacted_because`. +/// +/// See `From` implementations for ways to create an instance of this type. +#[derive(Clone, Debug)] +pub struct RedactedBecause(CanonicalJsonObject); + +impl From for RedactedBecause { + fn from(obj: CanonicalJsonObject) -> Self { + Self(obj) + } +} + +#[cfg(feature = "events")] +impl TryFrom<&Raw> for RedactedBecause { + type Error = serde_json::Error; + + fn try_from(value: &Raw) -> Result { + value.deserialize_as().map(Self) + } +} + +#[cfg(feature = "events")] +impl TryFrom<&Raw> for RedactedBecause { + type Error = serde_json::Error; + + fn try_from(value: &Raw) -> Result { + value.deserialize_as().map(Self) + } +} + /// Redacts an event using the rules specified in the Matrix client-server specification. /// /// This is part of the process of signing an event. @@ -127,7 +162,10 @@ pub fn to_canonical_value( /// /// # Parameters /// -/// * object: A JSON object to redact. +/// * `object`: A JSON object to redact. +/// * `version`: The room version, determines which keys to keep for a few event types. +/// * `redacted_because`: If this is set, an `unsigned` object with a `redacted_because` field set +/// to the given value is added to the event after redaction. /// /// # Errors /// @@ -140,8 +178,9 @@ pub fn to_canonical_value( pub fn redact( mut object: CanonicalJsonObject, version: &RoomVersionId, + redacted_because: Option, ) -> Result { - redact_in_place(&mut object, version)?; + redact_in_place(&mut object, version, redacted_because)?; Ok(object) } @@ -153,6 +192,7 @@ pub fn redact( pub fn redact_in_place( event: &mut CanonicalJsonObject, version: &RoomVersionId, + redacted_because: Option, ) -> Result<(), RedactionError> { // Get the content keys here even if they're only needed inside the branch below, because we // can't teach rust that this is a disjoint borrow with `get_mut("content")`. @@ -181,6 +221,14 @@ pub fn redact_in_place( } } + if let Some(redacted_because) = redacted_because { + let unsigned = CanonicalJsonObject::from_iter([( + "redacted_because".to_owned(), + redacted_because.0.into(), + )]); + event.insert("unsigned".to_owned(), unsigned.into()); + } + Ok(()) } diff --git a/crates/ruma-common/src/canonical_json/value.rs b/crates/ruma-common/src/canonical_json/value.rs index 64575925..f2168efd 100644 --- a/crates/ruma-common/src/canonical_json/value.rs +++ b/crates/ruma-common/src/canonical_json/value.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, fmt}; -use js_int::Int; +use js_int::{Int, UInt}; use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; use serde_json::{to_string as to_json_string, Value as JsonValue}; @@ -236,7 +236,7 @@ macro_rules! variant_impls { ($variant:ident($ty:ty)) => { impl From<$ty> for CanonicalJsonValue { fn from(val: $ty) -> Self { - Self::$variant(val) + Self::$variant(val.into()) } } @@ -263,9 +263,16 @@ macro_rules! variant_impls { variant_impls!(Bool(bool)); variant_impls!(Integer(Int)); variant_impls!(String(String)); +variant_impls!(String(&str)); variant_impls!(Array(Vec)); variant_impls!(Object(CanonicalJsonObject)); +impl From for CanonicalJsonValue { + fn from(value: UInt) -> Self { + Self::Integer(value.into()) + } +} + impl Serialize for CanonicalJsonValue { #[inline] fn serialize(&self, serializer: S) -> Result diff --git a/crates/ruma-signatures/src/functions.rs b/crates/ruma-signatures/src/functions.rs index 2f57305a..343a029f 100644 --- a/crates/ruma-signatures/src/functions.rs +++ b/crates/ruma-signatures/src/functions.rs @@ -321,7 +321,7 @@ pub fn reference_hash( value: &CanonicalJsonObject, version: &RoomVersionId, ) -> Result { - let redacted_value = redact(value.clone(), version)?; + let redacted_value = redact(value.clone(), version, None)?; let json = canonical_json_with_fields_to_remove(&redacted_value, REFERENCE_HASH_FIELDS_TO_REMOVE)?; @@ -458,7 +458,7 @@ where _ => return Err(JsonError::not_of_type("hashes", JsonType::Object)), }; - let mut redacted = redact(object.clone(), version)?; + let mut redacted = redact(object.clone(), version, None)?; sign_json(entity_id, key_pair, &mut redacted)?; @@ -539,7 +539,7 @@ pub fn verify_event( object: &CanonicalJsonObject, version: &RoomVersionId, ) -> Result { - let redacted = redact(object.clone(), version)?; + let redacted = redact(object.clone(), version, None)?; let hash = match object.get("hashes") { Some(hashes_value) => match hashes_value {