Address review feedback and finish canonical JSON move

This commit is contained in:
Jonas Platte 2022-06-20 13:28:53 +02:00 committed by Jonas Platte
parent 940450b953
commit a4a95e74fa
5 changed files with 105 additions and 43 deletions

View File

@ -36,6 +36,72 @@ impl fmt::Display for CanonicalJsonError {
impl std::error::Error for CanonicalJsonError {} impl std::error::Error for CanonicalJsonError {}
/// Errors that can happen in redaction.
#[cfg(feature = "canonical-json")]
#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum RedactionError {
/// The field `field` is not of the correct type `of_type` ([`JsonType`]).
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
NotOfType {
/// The field name.
field: String,
/// The expected JSON type.
of_type: JsonType,
},
/// The given required field is missing from a JSON object.
JsonFieldMissingFromObject(String),
}
impl fmt::Display for RedactionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RedactionError::NotOfType { field, of_type } => {
write!(f, "Value in {field:?} must be a JSON {of_type:?}")
}
RedactionError::JsonFieldMissingFromObject(field) => {
write!(f, "JSON object must contain the field {field:?}")
}
}
}
}
impl std::error::Error for RedactionError {}
impl RedactionError {
fn not_of_type(target: &str, of_type: JsonType) -> Self {
Self::NotOfType { field: target.to_owned(), of_type }
}
fn field_missing_from_object(target: &str) -> Self {
Self::JsonFieldMissingFromObject(target.to_owned())
}
}
/// A JSON type enum for [`RedactionError`] variants.
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum JsonType {
/// A JSON Object.
Object,
/// A JSON String.
String,
/// A JSON Integer.
Integer,
/// A JSON Array.
Array,
/// A JSON Boolean.
Boolean,
/// JSON Null.
Null,
}
/// Fallible conversion from a `serde_json::Map` to a `CanonicalJsonObject`. /// Fallible conversion from a `serde_json::Map` to a `CanonicalJsonObject`.
pub fn try_from_json_map( pub fn try_from_json_map(
json: serde_json::Map<String, JsonValue>, json: serde_json::Map<String, JsonValue>,
@ -74,7 +140,7 @@ pub fn to_canonical_value<T: Serialize>(
pub fn redact( pub fn redact(
object: &CanonicalJsonObject, object: &CanonicalJsonObject,
version: &RoomVersionId, version: &RoomVersionId,
) -> Result<CanonicalJsonObject, ()> { ) -> Result<CanonicalJsonObject, RedactionError> {
let mut val = object.clone(); let mut val = object.clone();
redact_in_place(&mut val, version)?; redact_in_place(&mut val, version)?;
Ok(val) Ok(val)
@ -85,21 +151,24 @@ pub fn redact(
/// Functionally equivalent to `redact`, only; /// Functionally equivalent to `redact`, only;
/// * upon error, the event is not touched. /// * upon error, the event is not touched.
/// * this'll redact the event in-place. /// * this'll redact the event in-place.
pub fn redact_in_place(event: &mut CanonicalJsonObject, version: &RoomVersionId) -> Result<(), ()> { pub fn redact_in_place(
event: &mut CanonicalJsonObject,
version: &RoomVersionId,
) -> Result<(), RedactionError> {
// Get the content keys here instead of the event type, because we cant teach rust that this is // Get the content keys here instead of the event type, because we cant teach rust that this is
// a disjoint borrow. // a disjoint borrow.
let allowed_content_keys: &[&str] = match event.get("type") { let allowed_content_keys: &[&str] = match event.get("type") {
Some(CanonicalJsonValue::String(event_type)) => { Some(CanonicalJsonValue::String(event_type)) => {
allowed_content_keys_for(event_type, version) allowed_content_keys_for(event_type, version)
} }
Some(_) => return Err(()), Some(_) => return Err(RedactionError::not_of_type("type", JsonType::String)),
None => return Err(()), None => return Err(RedactionError::field_missing_from_object("type")),
}; };
if let Some(content_value) = event.get_mut("content") { if let Some(content_value) = event.get_mut("content") {
let content = match content_value { let content = match content_value {
CanonicalJsonValue::Object(map) => map, CanonicalJsonValue::Object(map) => map,
_ => return Err(()), _ => return Err(RedactionError::not_of_type("content", JsonType::Object)),
}; };
object_retain_keys(content, allowed_content_keys); object_retain_keys(content, allowed_content_keys);

View File

@ -19,7 +19,7 @@ pub enum CanonicalJsonValue {
/// ///
/// ``` /// ```
/// # use serde_json::json; /// # use serde_json::json;
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!(null).try_into().unwrap(); /// let v: CanonicalJsonValue = json!(null).try_into().unwrap();
/// ``` /// ```
Null, Null,
@ -28,7 +28,7 @@ pub enum CanonicalJsonValue {
/// ///
/// ``` /// ```
/// # use serde_json::json; /// # use serde_json::json;
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!(true).try_into().unwrap(); /// let v: CanonicalJsonValue = json!(true).try_into().unwrap();
/// ``` /// ```
Bool(bool), Bool(bool),
@ -37,7 +37,7 @@ pub enum CanonicalJsonValue {
/// ///
/// ``` /// ```
/// # use serde_json::json; /// # use serde_json::json;
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!(12).try_into().unwrap(); /// let v: CanonicalJsonValue = json!(12).try_into().unwrap();
/// ``` /// ```
Integer(Int), Integer(Int),
@ -46,7 +46,7 @@ pub enum CanonicalJsonValue {
/// ///
/// ``` /// ```
/// # use serde_json::json; /// # use serde_json::json;
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!("a string").try_into().unwrap(); /// let v: CanonicalJsonValue = json!("a string").try_into().unwrap();
/// ``` /// ```
String(String), String(String),
@ -55,7 +55,7 @@ pub enum CanonicalJsonValue {
/// ///
/// ``` /// ```
/// # use serde_json::json; /// # use serde_json::json;
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap(); /// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap();
/// ``` /// ```
Array(Vec<CanonicalJsonValue>), Array(Vec<CanonicalJsonValue>),
@ -66,7 +66,7 @@ pub enum CanonicalJsonValue {
/// ///
/// ``` /// ```
/// # use serde_json::json; /// # use serde_json::json;
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap(); /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
/// ``` /// ```
Object(CanonicalJsonObject), Object(CanonicalJsonObject),

View File

@ -1,5 +1,7 @@
use ruma_common::{ use ruma_common::{
serde::Base64DecodeError, EventId, OwnedEventId, OwnedServerName, RoomVersionId, canonical_json::{JsonType, RedactionError},
serde::Base64DecodeError,
EventId, OwnedEventId, OwnedServerName, RoomVersionId,
}; };
use thiserror::Error; use thiserror::Error;
@ -41,11 +43,25 @@ pub enum Error {
PduSize, PduSize,
} }
impl From<RedactionError> for Error {
fn from(err: RedactionError) -> Self {
match err {
RedactionError::NotOfType { field: target, of_type, .. } => {
JsonError::NotOfType { target, of_type }.into()
}
RedactionError::JsonFieldMissingFromObject(field) => {
JsonError::JsonFieldMissingFromObject(field).into()
}
_ => unreachable!(),
}
}
}
/// All errors related to JSON validation/parsing. /// All errors related to JSON validation/parsing.
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum JsonError { pub enum JsonError {
/// Signals that `target` is not of type `of_type` ([`JsonType`]). /// `target` is not of the correct type `of_type` ([`JsonType`]).
#[error("Value in {target:?} must be a JSON {of_type:?}")] #[error("Value in {target:?} must be a JSON {of_type:?}")]
NotOfType { NotOfType {
/// An arbitrary "target" that doesn't have the required type. /// An arbitrary "target" that doesn't have the required type.
@ -65,11 +81,11 @@ pub enum JsonError {
of_type: JsonType, of_type: JsonType,
}, },
/// Signals that a specific field is missing from a JSON object. /// The given required field is missing from a JSON object.
#[error("JSON object must contain the field {0:?}")] #[error("JSON object must contain the field {0:?}")]
JsonFieldMissingFromObject(String), JsonFieldMissingFromObject(String),
/// Signals a key missing from a JSON object. /// A key is missing from a JSON object.
/// ///
/// Note that this is different from [`JsonError::JsonFieldMissingFromObject`], /// Note that this is different from [`JsonError::JsonFieldMissingFromObject`],
/// this error talks about an expected identifying key (`"ed25519:abcd"`) /// this error talks about an expected identifying key (`"ed25519:abcd"`)
@ -117,29 +133,6 @@ impl JsonError {
} }
} }
/// A JSON type enum for [`JsonError`] variants.
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum JsonType {
/// A JSON Object.
Object,
/// A JSON String.
String,
/// A JSON Integer.
Integer,
/// A JSON Array.
Array,
/// A JSON Boolean.
Boolean,
/// JSON Null.
Null,
}
/// Errors relating to verification of events and signatures. /// Errors relating to verification of events and signatures.
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[non_exhaustive] #[non_exhaustive]

View File

@ -8,6 +8,7 @@ use std::{
use base64::{encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD}; use base64::{encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
use ruma_common::{ use ruma_common::{
canonical_json::{redact, JsonType},
serde::{base64::Standard, Base64}, serde::{base64::Standard, Base64},
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedServerName, RoomVersionId, UserId, CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedServerName, RoomVersionId, UserId,
}; };
@ -18,7 +19,7 @@ use crate::{
keys::{KeyPair, PublicKeyMap}, keys::{KeyPair, PublicKeyMap},
split_id, split_id,
verification::{Ed25519Verifier, Verified, Verifier}, verification::{Ed25519Verifier, Verified, Verifier},
Error, JsonError, JsonType, ParseError, VerificationError, Error, JsonError, ParseError, VerificationError,
}; };
const MAX_PDU_BYTES: usize = 65_535; const MAX_PDU_BYTES: usize = 65_535;

View File

@ -46,13 +46,12 @@
use ruma_common::serde::{AsRefStr, DisplayAsRefStr}; use ruma_common::serde::{AsRefStr, DisplayAsRefStr};
pub use error::{Error, JsonError, JsonType, ParseError, VerificationError}; pub use error::{Error, JsonError, ParseError, VerificationError};
pub use functions::{ pub use functions::{
canonical_json, content_hash, hash_and_sign_event, redact, redact_content_in_place, canonical_json, content_hash, hash_and_sign_event, reference_hash, sign_json, verify_event,
redact_in_place, reference_hash, sign_json, verify_event, verify_json, verify_json,
}; };
pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet}; pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet};
pub use ruma_common::{CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue};
pub use signatures::Signature; pub use signatures::Signature;
pub use verification::Verified; pub use verification::Verified;