events: Add unstable support for secret sharing
This commit is contained in:
parent
c24890f82b
commit
0768f551c3
@ -38,6 +38,7 @@ Improvements:
|
||||
|
||||
* Add unstable blurhash field to member event content struct
|
||||
* Add constructors for the unstable spaces parent and child event content types
|
||||
* Add unstable support for `m.secret.request` and `m.secret.send` events.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
|
@ -112,6 +112,12 @@ event_enum! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
"m.key.verification.done",
|
||||
"m.room.encrypted",
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
"m.secret.request",
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
"m.secret.send",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,9 @@ pub mod room_key;
|
||||
pub mod room_key_request;
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
pub mod secret;
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
pub mod space;
|
||||
pub mod sticker;
|
||||
pub mod tag;
|
||||
|
4
crates/ruma-events/src/secret.rs
Normal file
4
crates/ruma-events/src/secret.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! Module for events in the *m.secret* namespace.
|
||||
|
||||
pub mod request;
|
||||
pub mod send;
|
300
crates/ruma-events/src/secret/request.rs
Normal file
300
crates/ruma-events/src/secret/request.rs
Normal file
@ -0,0 +1,300 @@
|
||||
//! Types for the *m.secret.request* event.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use ruma_events_macros::EventContent;
|
||||
use ruma_identifiers::DeviceIdBox;
|
||||
use ruma_serde::StringEnum;
|
||||
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||
|
||||
use crate::ToDeviceEvent;
|
||||
|
||||
/// Event sent by a client to request a secret from another device or to cancel a previous request.
|
||||
///
|
||||
/// It is sent as an unencrypted to-device event.
|
||||
pub type RequestToDeviceEvent = ToDeviceEvent<RequestToDeviceEventContent>;
|
||||
|
||||
/// The payload for RequestToDeviceEvent.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.secret.request", kind = ToDevice)]
|
||||
pub struct RequestToDeviceEventContent {
|
||||
/// The action for the request.
|
||||
#[serde(flatten)]
|
||||
pub action: RequestAction,
|
||||
|
||||
/// The ID of the device requesting the event.
|
||||
pub requesting_device_id: DeviceIdBox,
|
||||
|
||||
/// A random string uniquely identifying (with respect to the requester and the target) the
|
||||
/// target for a secret.
|
||||
///
|
||||
/// If the secret is requested from multiple devices at the same time, the same ID may be used
|
||||
/// for every target. The same ID is also used in order to cancel a previous request.
|
||||
pub request_id: String,
|
||||
}
|
||||
|
||||
impl RequestToDeviceEventContent {
|
||||
/// Creates a new `RequestToDeviceEventContent` with the given action, requesting device ID and
|
||||
/// request ID.
|
||||
pub fn new(
|
||||
action: RequestAction,
|
||||
requesting_device_id: DeviceIdBox,
|
||||
request_id: String,
|
||||
) -> Self {
|
||||
Self { action, requesting_device_id, request_id }
|
||||
}
|
||||
}
|
||||
|
||||
/// Action for an *m.secret.request* event.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(try_from = "RequestActionJsonRepr")]
|
||||
pub enum RequestAction {
|
||||
/// Request a secret by its name.
|
||||
Request(SecretName),
|
||||
|
||||
/// Cancel a request for a secret.
|
||||
RequestCancellation,
|
||||
|
||||
#[doc(hidden)]
|
||||
_Custom(String),
|
||||
}
|
||||
|
||||
impl Serialize for RequestAction {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut st = serializer.serialize_struct("request_action", 2)?;
|
||||
|
||||
match self {
|
||||
Self::Request(name) => {
|
||||
st.serialize_field("name", name)?;
|
||||
st.serialize_field("action", "request")?;
|
||||
st.end()
|
||||
}
|
||||
Self::RequestCancellation => {
|
||||
st.serialize_field("action", "request_cancellation")?;
|
||||
st.end()
|
||||
}
|
||||
RequestAction::_Custom(custom) => {
|
||||
st.serialize_field("action", custom)?;
|
||||
st.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RequestActionJsonRepr {
|
||||
action: String,
|
||||
name: Option<SecretName>,
|
||||
}
|
||||
|
||||
impl TryFrom<RequestActionJsonRepr> for RequestAction {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: RequestActionJsonRepr) -> Result<Self, Self::Error> {
|
||||
match value.action.as_str() {
|
||||
"request" => {
|
||||
if let Some(name) = value.name {
|
||||
Ok(RequestAction::Request(name))
|
||||
} else {
|
||||
Err("A secret name is required when the action is \"request\".")
|
||||
}
|
||||
}
|
||||
"request_cancellation" => Ok(RequestAction::RequestCancellation),
|
||||
_ => Ok(RequestAction::_Custom(value.action)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of a secret.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum SecretName {
|
||||
/// Cross-signing master key (m.cross_signing.master).
|
||||
#[ruma_enum(rename = "m.cross_signing.master")]
|
||||
CrossSigningMasterKey,
|
||||
|
||||
/// Cross-signing user-signing key (m.cross_signing.user_signing).
|
||||
#[ruma_enum(rename = "m.cross_signing.user_signing")]
|
||||
CrossSigningUserSigningKey,
|
||||
|
||||
/// Cross-signing self-signing key (m.cross_signing.self_signing).
|
||||
#[ruma_enum(rename = "m.cross_signing.self_signing")]
|
||||
CrossSigningSelfSigningKey,
|
||||
|
||||
/// Recovery key (m.megolm_backup.v1).
|
||||
#[ruma_enum(rename = "m.megolm_backup.v1")]
|
||||
RecoveryKey,
|
||||
|
||||
#[doc(hidden)]
|
||||
_Custom(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{RequestAction, RequestToDeviceEventContent, SecretName};
|
||||
use matches::assert_matches;
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
#[test]
|
||||
fn secret_request_serialization() {
|
||||
let content = RequestToDeviceEventContent::new(
|
||||
RequestAction::Request("org.example.some.secret".into()),
|
||||
"ABCDEFG".into(),
|
||||
"randomly_generated_id_9573".into(),
|
||||
);
|
||||
|
||||
let json = json!({
|
||||
"name": "org.example.some.secret",
|
||||
"action": "request",
|
||||
"requesting_device_id": "ABCDEFG",
|
||||
"request_id": "randomly_generated_id_9573"
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_request_recovery_key_serialization() {
|
||||
let content = RequestToDeviceEventContent::new(
|
||||
RequestAction::Request(SecretName::RecoveryKey),
|
||||
"XYZxyz".into(),
|
||||
"this_is_a_request_id".into(),
|
||||
);
|
||||
|
||||
let json = json!({
|
||||
"name": "m.megolm_backup.v1",
|
||||
"action": "request",
|
||||
"requesting_device_id": "XYZxyz",
|
||||
"request_id": "this_is_a_request_id"
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_custom_action_serialization() {
|
||||
let content = RequestToDeviceEventContent::new(
|
||||
RequestAction::_Custom("my_custom_action".into()),
|
||||
"XYZxyz".into(),
|
||||
"this_is_a_request_id".into(),
|
||||
);
|
||||
|
||||
let json = json!({
|
||||
"action": "my_custom_action",
|
||||
"requesting_device_id": "XYZxyz",
|
||||
"request_id": "this_is_a_request_id"
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_request_cancellation_serialization() {
|
||||
let content = RequestToDeviceEventContent::new(
|
||||
RequestAction::RequestCancellation,
|
||||
"ABCDEFG".into(),
|
||||
"randomly_generated_id_9573".into(),
|
||||
);
|
||||
|
||||
let json = json!({
|
||||
"action": "request_cancellation",
|
||||
"requesting_device_id": "ABCDEFG",
|
||||
"request_id": "randomly_generated_id_9573"
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_request_deserialization() {
|
||||
let json = json!({
|
||||
"name": "org.example.some.secret",
|
||||
"action": "request",
|
||||
"requesting_device_id": "ABCDEFG",
|
||||
"request_id": "randomly_generated_id_9573"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value(json).unwrap(),
|
||||
RequestToDeviceEventContent {
|
||||
action: RequestAction::Request(
|
||||
secret
|
||||
),
|
||||
requesting_device_id,
|
||||
request_id,
|
||||
}
|
||||
if secret == "org.example.some.secret".into()
|
||||
&& requesting_device_id == "ABCDEFG"
|
||||
&& request_id == "randomly_generated_id_9573"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_request_cancellation_deserialization() {
|
||||
let json = json!({
|
||||
"action": "request_cancellation",
|
||||
"requesting_device_id": "ABCDEFG",
|
||||
"request_id": "randomly_generated_id_9573"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value(json).unwrap(),
|
||||
RequestToDeviceEventContent {
|
||||
action: RequestAction::RequestCancellation,
|
||||
requesting_device_id,
|
||||
request_id,
|
||||
}
|
||||
if requesting_device_id.as_str() == "ABCDEFG"
|
||||
&& request_id == "randomly_generated_id_9573"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_request_recovery_key_deserialization() {
|
||||
let json = json!({
|
||||
"name": "m.megolm_backup.v1",
|
||||
"action": "request",
|
||||
"requesting_device_id": "XYZxyz",
|
||||
"request_id": "this_is_a_request_id"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value(json).unwrap(),
|
||||
RequestToDeviceEventContent {
|
||||
action: RequestAction::Request(
|
||||
SecretName::RecoveryKey
|
||||
),
|
||||
requesting_device_id,
|
||||
request_id,
|
||||
}
|
||||
if requesting_device_id == "XYZxyz"
|
||||
&& request_id == "this_is_a_request_id"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_custom_action_deserialization() {
|
||||
let json = json!({
|
||||
"action": "my_custom_action",
|
||||
"requesting_device_id": "XYZxyz",
|
||||
"request_id": "this_is_a_request_id"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<RequestToDeviceEventContent>(json).unwrap(),
|
||||
RequestToDeviceEventContent {
|
||||
action,
|
||||
requesting_device_id,
|
||||
request_id,
|
||||
}
|
||||
if action == RequestAction::_Custom("my_custom_action".into())
|
||||
&& requesting_device_id == "XYZxyz"
|
||||
&& request_id == "this_is_a_request_id"
|
||||
)
|
||||
}
|
||||
}
|
31
crates/ruma-events/src/secret/send.rs
Normal file
31
crates/ruma-events/src/secret/send.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//! Types for the *m.secret.send* event.
|
||||
|
||||
use ruma_events_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ToDeviceEvent;
|
||||
|
||||
/// An event sent by a client to share a secret with another device, in response to an
|
||||
/// `m.secret.request` event.
|
||||
///
|
||||
/// It must be encrypted as an `m.room.encrypted` event, then sent as a to-device event.
|
||||
pub type SendToDeviceEvent = ToDeviceEvent<SendToDeviceEventContent>;
|
||||
|
||||
/// The payload for `SendToDeviceEvent`.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.secret.send", kind = ToDevice)]
|
||||
pub struct SendToDeviceEventContent {
|
||||
/// The ID of the request that this is a response to.
|
||||
pub request_id: String,
|
||||
|
||||
/// The contents of the secret.
|
||||
pub secret: String,
|
||||
}
|
||||
|
||||
impl SendToDeviceEventContent {
|
||||
/// Creates a new `SecretSendEventContent` with the given request ID and secret.
|
||||
pub fn new(request_id: String, secret: String) -> Self {
|
||||
Self { request_id, secret }
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user