From 1db0082281f80ea9226a95c9a39669e8865e9c10 Mon Sep 17 00:00:00 2001 From: "Ragotzy.devin" Date: Thu, 16 Jul 2020 19:07:07 -0400 Subject: [PATCH] Add redact method to all event_enum! generated enums MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … and to AliasesEventContent --- ruma-events-macros/src/event_content.rs | 2 +- ruma-events-macros/src/event_enum.rs | 107 ++++++++++++++++++++++++ ruma-events/src/custom.rs | 3 +- ruma-events/src/room/aliases.rs | 18 +++- ruma-events/tests/redacted.rs | 59 +++++++++++-- 5 files changed, 178 insertions(+), 11 deletions(-) diff --git a/ruma-events-macros/src/event_content.rs b/ruma-events-macros/src/event_content.rs index fc51ff07..a4226dc9 100644 --- a/ruma-events-macros/src/event_content.rs +++ b/ruma-events-macros/src/event_content.rs @@ -153,7 +153,7 @@ pub fn expand_event_content(input: &DeriveInput, emit_redacted: bool) -> syn::Re // this is the non redacted event content's impl impl #ident { /// Transforms the full event content into a redacted content according to spec. - pub fn redact(self) -> #redacted_ident { + pub fn redact(self, version: ::ruma_identifiers::RoomVersionId) -> #redacted_ident { #redacted_ident { #( #redaction_struct_fields: self.#redaction_struct_fields, )* } } } diff --git a/ruma-events-macros/src/event_enum.rs b/ruma-events-macros/src/event_enum.rs index 4487139a..31e7f23f 100644 --- a/ruma-events-macros/src/event_enum.rs +++ b/ruma-events-macros/src/event_enum.rs @@ -131,15 +131,122 @@ fn expand_any_with_deser( let field_accessor_impl = accessor_methods(kind, var, &variants); + let redact_impl = generate_redact(&ident, kind, var, &variants); + Some(quote! { #any_enum #field_accessor_impl + #redact_impl + #event_deserialize_impl }) } +fn generate_redact( + ident: &Ident, + kind: &EventKind, + var: &EventKindVariation, + variants: &[Ident], +) -> Option { + if let EventKindVariation::Full | EventKindVariation::Sync | EventKindVariation::Stripped = var + { + let (param, redaction_type, redaction_enum) = match var { + EventKindVariation::Full => { + let struct_id = kind.to_event_ident(&EventKindVariation::Redacted)?; + ( + quote! { ::ruma_events::room::redaction::RedactionEvent }, + quote! { ::ruma_events::#struct_id }, + kind.to_event_enum_ident(&EventKindVariation::Redacted)?, + ) + } + EventKindVariation::Sync => { + let struct_id = kind.to_event_ident(&EventKindVariation::RedactedSync)?; + ( + quote! { ::ruma_events::room::redaction::SyncRedactionEvent }, + quote! { ::ruma_events::#struct_id }, + kind.to_event_enum_ident(&EventKindVariation::RedactedSync)?, + ) + } + EventKindVariation::Stripped => { + let struct_id = kind.to_event_ident(&EventKindVariation::RedactedStripped)?; + ( + quote! { ::ruma_events::room::redaction::SyncRedactionEvent }, + quote! { ::ruma_events::#struct_id }, + kind.to_event_enum_ident(&EventKindVariation::RedactedStripped)?, + ) + } + _ => return None, + }; + + let fields = EVENT_FIELDS + .iter() + .map(|(name, has_field)| generate_redacted_fields(name, kind, var, *has_field)); + + let fields = quote! { #( #fields )* }; + + Some(quote! { + impl #ident { + /// Redacts `Self` given a valid `Redaction[Sync]Event`. + pub fn redact(self, redaction: #param, version: ::ruma_identifiers::RoomVersionId) -> #redaction_enum { + match self { + #( + Self::#variants(event) => { + let content = event.content.redact(version); + #redaction_enum::#variants(#redaction_type { + content, + #fields + }) + } + )* + Self::Custom(event) => { + let content = event.content.redact(version); + #redaction_enum::Custom(#redaction_type { + content, + #fields + }) + } + } + } + } + }) + } else { + None + } +} + +fn generate_redacted_fields( + name: &str, + kind: &EventKind, + var: &EventKindVariation, + is_event_kind: EventKindFn, +) -> TokenStream { + if is_event_kind(kind, var) { + let name = Ident::new(name, Span::call_site()); + + if name == "unsigned" { + let redaction_type = if let EventKindVariation::Sync = var { + quote! { RedactedSyncUnsigned } + } else { + quote! { RedactedUnsigned } + }; + + quote! { + unsigned: ::ruma_events::#redaction_type { + redacted_because: Some(::ruma_events::EventJson::from(redaction)), + }, + } + } else { + quote! { + #name: event.#name, + } + } + } else { + TokenStream::new() + } +} + /// Generates the 3 redacted state enums, 2 redacted message enums, /// and `Deserialize` implementations. /// diff --git a/ruma-events/src/custom.rs b/ruma-events/src/custom.rs index b17e20bc..394412c7 100644 --- a/ruma-events/src/custom.rs +++ b/ruma-events/src/custom.rs @@ -1,5 +1,6 @@ //! Types for custom events outside of the Matrix specification. +use ruma_identifiers::RoomVersionId; use serde::Serialize; use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue}; @@ -23,7 +24,7 @@ pub struct CustomEventContent { impl CustomEventContent { /// Transforms the full event content into a redacted content according to spec. - pub fn redact(self) -> RedactedCustomEventContent { + pub fn redact(self, _: RoomVersionId) -> RedactedCustomEventContent { RedactedCustomEventContent { event_type: self.event_type } } } diff --git a/ruma-events/src/room/aliases.rs b/ruma-events/src/room/aliases.rs index a5d4610d..0f0cd61d 100644 --- a/ruma-events/src/room/aliases.rs +++ b/ruma-events/src/room/aliases.rs @@ -1,7 +1,7 @@ //! Types for the *m.room.aliases* event. use ruma_events_macros::StateEventContent; -use ruma_identifiers::RoomAliasId; +use ruma_identifiers::{RoomAliasId, RoomVersionId}; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue as RawJsonValue; @@ -27,6 +27,22 @@ impl AliasesEventContent { pub fn new(aliases: Vec) -> Self { Self { aliases } } + + /// Redact an `AliasesEventContent` according to current Matrix spec. + pub fn redact(self, version: RoomVersionId) -> RedactedAliasesEventContent { + // We compare the long way to avoid pre version 6 behavior if/when + // a new room version is introduced. + if version.is_version_1() + || version.is_version_2() + || version.is_version_3() + || version.is_version_4() + || version.is_version_5() + { + RedactedAliasesEventContent { aliases: Some(self.aliases) } + } else { + RedactedAliasesEventContent { aliases: None } + } + } } /// An aliases event that has been redacted. diff --git a/ruma-events/tests/redacted.rs b/ruma-events/tests/redacted.rs index cad50308..cf96dc3f 100644 --- a/ruma-events/tests/redacted.rs +++ b/ruma-events/tests/redacted.rs @@ -13,11 +13,12 @@ use ruma_events::{ message::RedactedMessageEventContent, redaction::{RedactionEvent, RedactionEventContent, SyncRedactionEvent}, }, - AnyRedactedMessageEvent, AnyRedactedSyncMessageEvent, AnyRedactedSyncStateEvent, AnyRoomEvent, - AnySyncRoomEvent, RedactedMessageEvent, RedactedSyncMessageEvent, RedactedSyncStateEvent, - RedactedSyncUnsigned, RedactedUnsigned, Unsigned, + AnyMessageEvent, AnyRedactedMessageEvent, AnyRedactedSyncMessageEvent, + AnyRedactedSyncStateEvent, AnyRoomEvent, AnySyncRoomEvent, RedactedMessageEvent, + RedactedSyncMessageEvent, RedactedSyncStateEvent, RedactedSyncUnsigned, RedactedUnsigned, + Unsigned, }; -use ruma_identifiers::{EventId, RoomId, UserId}; +use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId}; use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; fn full_unsigned() -> RedactedSyncUnsigned { @@ -263,10 +264,8 @@ fn redacted_custom_event_serialize() { && event_type == "m.made.up" ); - let x = from_json_value::>(redacted) - .unwrap() - .deserialize() - .unwrap(); + let x = + from_json_value::>(redacted).unwrap().deserialize().unwrap(); assert_eq!(x.event_id(), &EventId::try_from("$h29iv0s8:example.com").unwrap()) } @@ -295,3 +294,47 @@ fn redacted_custom_event_deserialize() { let actual = to_json_value(&redacted).unwrap(); assert_eq!(actual, expected); } + +#[test] +fn redact_method_properly_redacts() { + let ev = json!({ + "type": "m.room.message", + "event_id": "$143273582443PhrSn:example.com", + "origin_server_ts": 1, + "room_id": "!roomid:room.com", + "sender": "@user:example.com", + "content": { + "body": "test", + "msgtype": "m.audio", + "url": "http://example.com/audio.mp3", + } + }); + + let redaction = RedactionEvent { + content: RedactionEventContent { reason: Some("redacted because".into()) }, + redacts: EventId::try_from("$143273582443PhrSn:example.com").unwrap(), + event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(), + origin_server_ts: UNIX_EPOCH + Duration::from_millis(1), + room_id: RoomId::try_from("!roomid:room.com").unwrap(), + sender: UserId::try_from("@carl:example.com").unwrap(), + unsigned: Unsigned::default(), + }; + + let event = from_json_value::>(ev).unwrap().deserialize().unwrap(); + + assert_matches!( + event.redact(redaction, RoomVersionId::version_6()), + AnyRedactedMessageEvent::RoomMessage(RedactedMessageEvent { + content: RedactedMessageEventContent, + event_id, + room_id, + sender, + origin_server_ts, + unsigned, + }) if event_id == EventId::try_from("$143273582443PhrSn:example.com").unwrap() + && unsigned.redacted_because.is_some() + && room_id == RoomId::try_from("!roomid:room.com").unwrap() + && sender == UserId::try_from("@user:example.com").unwrap() + && origin_server_ts == UNIX_EPOCH + Duration::from_millis(1) + ); +}