Skip to content

Commit

Permalink
Add support for the dehydrated devices endpoints (#1605)
Browse files Browse the repository at this point in the history
This patch adds support for the endpoints used in [MSC3814].

One notable change to the MSC here is that the PUT endpoint uploads the
device and one-time keys as well.

[MSC3814]: matrix-org/matrix-spec-proposals#3814

Co-authored-by: Kévin Commaille <76261501+zecakeh@users.noreply.github.com>
  • Loading branch information
poljar and zecakeh committed Aug 9, 2023
1 parent 3bd58e3 commit 0a82459
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/ruma-client-api/Cargo.toml
Expand Up @@ -45,6 +45,7 @@ unstable-msc2965 = []
unstable-msc2967 = []
unstable-msc3488 = []
unstable-msc3575 = []
unstable-msc3814 = []

[dependencies]
assign = { workspace = true }
Expand Down
88 changes: 88 additions & 0 deletions crates/ruma-client-api/src/dehydrated_device.rs
@@ -0,0 +1,88 @@
//! Endpoints for managing dehydrated devices.

use ruma_common::serde::StringEnum;
use serde::{Deserialize, Serialize};

use crate::PrivOwnedStr;

pub mod delete_dehydrated_device;
pub mod get_dehydrated_device;
pub mod get_events;
pub mod put_dehydrated_device;

/// Data for a dehydrated device.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(try_from = "Helper", into = "Helper")]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum DehydratedDeviceData {
/// The `org.matrix.msc3814.v1.olm` variant of a dehydrated device.
V1(DehydratedDeviceV1),
}

impl DehydratedDeviceData {
/// Get the algorithm this dehydrated device uses.
pub fn algorithm(&self) -> DeviceDehydrationAlgorithm {
match self {
DehydratedDeviceData::V1(_) => DeviceDehydrationAlgorithm::V1,
}
}
}

/// The `org.matrix.msc3814.v1.olm` variant of a dehydrated device.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct DehydratedDeviceV1 {
/// The pickle of the `Olm` account of the device.
///
/// The pickle will contain the private parts of the long-term identity keys of the device as
/// well as a collection of one-time keys.
pub device_pickle: String,
}

impl DehydratedDeviceV1 {
/// Create a [`DehydratedDeviceV1`] struct from a device pickle.
pub fn new(device_pickle: String) -> Self {
Self { device_pickle }
}
}

/// The algorithms used for dehydrated devices.
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(Clone, PartialEq, Eq, StringEnum)]
#[non_exhaustive]
pub enum DeviceDehydrationAlgorithm {
/// The `org.matrix.msc3814.v1.olm` device dehydration algorithm.
#[ruma_enum(rename = "org.matrix.msc3814.v1.olm")]
V1,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}

#[derive(Deserialize, Serialize)]
struct Helper {
algorithm: DeviceDehydrationAlgorithm,
device_pickle: String,
}

impl TryFrom<Helper> for DehydratedDeviceData {
type Error = serde_json::Error;

fn try_from(value: Helper) -> Result<Self, Self::Error> {
match value.algorithm {
DeviceDehydrationAlgorithm::V1 => Ok(DehydratedDeviceData::V1(DehydratedDeviceV1 {
device_pickle: value.device_pickle,
})),
_ => Err(serde::de::Error::custom("Unsupported device dehydration algorithm.")),
}
}
}

impl From<DehydratedDeviceData> for Helper {
fn from(value: DehydratedDeviceData) -> Self {
let algorithm = value.algorithm();

match value {
DehydratedDeviceData::V1(d) => Self { algorithm, device_pickle: d.device_pickle },
}
}
}
@@ -0,0 +1,48 @@
//! `DELETE /_matrix/client/*/dehydrated_device/`
//!
//! Delete a dehydrated device.

pub mod unstable {
//! `msc3814` ([MSC])
//!
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3814

use ruma_common::{
api::{request, response, Metadata},
metadata, OwnedDeviceId,
};

const METADATA: Metadata = metadata! {
method: DELETE,
rate_limited: false,
authentication: AccessToken,
history: {
unstable => "/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device",
}
};

/// Request type for the `DELETE` `dehydrated_device` endpoint.
#[request(error = crate::Error)]
pub struct Request {}

/// Request type for the `DELETE` `dehydrated_device` endpoint.
#[response(error = crate::Error)]
pub struct Response {
/// The unique ID of the device that was deleted.
pub device_id: OwnedDeviceId,
}

impl Request {
/// Creates a new empty `Request`.
pub fn new() -> Self {
Self {}
}
}

impl Response {
/// Creates a new `Response` with the given device ID.
pub fn new(device_id: OwnedDeviceId) -> Self {
Self { device_id }
}
}
}
@@ -0,0 +1,54 @@
//! `GET /_matrix/client/*/dehydrated_device/`
//!
//! Get a dehydrated device for rehydration.

pub mod unstable {
//! `msc3814` ([MSC])
//!
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3814

use ruma_common::{
api::{request, response, Metadata},
metadata,
serde::Raw,
OwnedDeviceId,
};

use crate::dehydrated_device::DehydratedDeviceData;

const METADATA: Metadata = metadata! {
method: GET,
rate_limited: false,
authentication: AccessToken,
history: {
unstable => "/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device",
}
};

/// Request type for the `GET` `dehydrated_device` endpoint.
#[request(error = crate::Error)]
pub struct Request {}

/// Request type for the `GET` `dehydrated_device` endpoint.
#[response(error = crate::Error)]
pub struct Response {
/// The unique ID of the device.
pub device_id: OwnedDeviceId,
/// Information about the device.
pub device_data: Raw<DehydratedDeviceData>,
}

impl Request {
/// Creates a new empty `Request`.
pub fn new() -> Self {
Self {}
}
}

impl Response {
/// Creates a new `Response` with the given device ID and device data.
pub fn new(device_id: OwnedDeviceId, device_data: Raw<DehydratedDeviceData>) -> Self {
Self { device_id, device_data }
}
}
}
65 changes: 65 additions & 0 deletions crates/ruma-client-api/src/dehydrated_device/get_events.rs
@@ -0,0 +1,65 @@
//! `POST /_matrix/client/*/dehydrated_device/{device_id}/events`
//!
//! Get to-device events for a dehydrated device.

pub mod unstable {
//! `msc3814` ([MSC])
//!
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3814

use ruma_common::{
api::{request, response, Metadata},
events::AnyToDeviceEvent,
metadata,
serde::Raw,
OwnedDeviceId,
};

const METADATA: Metadata = metadata! {
method: POST,
rate_limited: false,
authentication: AccessToken,
history: {
unstable => "/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device/:device_id/events",
}
};

/// Request type for the `dehydrated_device/{device_id}/events` endpoint.
#[request(error = crate::Error)]
pub struct Request {
/// The unique ID of the device for which we would like to fetch events.
#[ruma_api(path)]
pub device_id: OwnedDeviceId,
/// A point in time to continue getting events from.
///
/// Should be a token from the `next_batch` field of a previous `/events`
/// request.
#[serde(skip_serializing_if = "Option::is_none")]
pub next_batch: Option<String>,
}

/// Request type for the `dehydrated_device/{device_id}/events` endpoint.
#[response(error = crate::Error)]
pub struct Response {
/// The batch token to supply in the `since` param of the next `/events` request. Will be
/// none if no further events can be found.
pub next_batch: Option<String>,

/// Messages sent directly between devices.
pub events: Vec<Raw<AnyToDeviceEvent>>,
}

impl Request {
/// Create a new request.
pub fn new(device_id: OwnedDeviceId) -> Self {
Self { device_id, next_batch: None }
}
}

impl Response {
/// Create a new response with the given events.
pub fn new(events: Vec<Raw<AnyToDeviceEvent>>) -> Self {
Self { next_batch: None, events }
}
}
}
@@ -0,0 +1,87 @@
//! `PUT /_matrix/client/*/dehydrated_device/`
//!
//! Uploads a dehydrated device to the homeserver.

pub mod unstable {
//! `msc3814` ([MSC])
//!
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3814

use std::collections::BTreeMap;

use ruma_common::{
api::{request, response, Metadata},
encryption::{DeviceKeys, OneTimeKey},
metadata,
serde::Raw,
OwnedDeviceId, OwnedDeviceKeyId,
};

use crate::dehydrated_device::DehydratedDeviceData;

const METADATA: Metadata = metadata! {
method: PUT,
rate_limited: false,
authentication: AccessToken,
history: {
unstable => "/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device",
}
};

/// Request type for the `PUT` `dehydrated_device` endpoint.
#[request(error = crate::Error)]
pub struct Request {
/// The unique ID of the device.
pub device_id: OwnedDeviceId,

/// The display name of the device.
pub initial_device_display_name: Option<String>,

/// The data of the dehydrated device, containing the serialized and encrypted private
/// parts of the [`DeviceKeys`].
pub device_data: Raw<DehydratedDeviceData>,

/// Identity keys for the dehydrated device.
pub device_keys: Raw<DeviceKeys>,

/// One-time public keys for "pre-key" messages.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub one_time_keys: BTreeMap<OwnedDeviceKeyId, Raw<OneTimeKey>>,

/// Fallback public keys for "pre-key" messages.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub fallback_keys: BTreeMap<OwnedDeviceKeyId, Raw<OneTimeKey>>,
}

/// Response type for the `upload_keys` endpoint.
#[response(error = crate::Error)]
pub struct Response {
/// The unique ID of the device.
pub device_id: OwnedDeviceId,
}

impl Request {
/// Creates a new Request.
pub fn new(
device_id: OwnedDeviceId,
device_data: Raw<DehydratedDeviceData>,
device_keys: Raw<DeviceKeys>,
) -> Self {
Self {
device_id,
device_data,
device_keys,
initial_device_display_name: None,
one_time_keys: Default::default(),
fallback_keys: Default::default(),
}
}
}

impl Response {
/// Creates a new `Response` with the given one time key counts.
pub fn new(device_id: OwnedDeviceId) -> Self {
Self { device_id }
}
}
}
2 changes: 2 additions & 0 deletions crates/ruma-client-api/src/lib.rs
Expand Up @@ -15,6 +15,8 @@ pub mod appservice;
pub mod backup;
pub mod config;
pub mod context;
#[cfg(feature = "unstable-msc3814")]
pub mod dehydrated_device;
pub mod device;
pub mod directory;
pub mod discovery;
Expand Down
2 changes: 2 additions & 0 deletions crates/ruma/Cargo.toml
Expand Up @@ -181,6 +181,7 @@ unstable-msc3554 = ["ruma-common/unstable-msc3554"]
unstable-msc3575 = ["ruma-client-api?/unstable-msc3575"]
unstable-msc3618 = ["ruma-federation-api?/unstable-msc3618"]
unstable-msc3723 = ["ruma-federation-api?/unstable-msc3723"]
unstable-msc3814 = ["ruma-client-api?/unstable-msc3814"]
unstable-msc3927 = ["ruma-common/unstable-msc3927"]
unstable-msc3931 = ["ruma-common/unstable-msc3931"]
unstable-msc3932 = ["ruma-common/unstable-msc3932"]
Expand Down Expand Up @@ -222,6 +223,7 @@ __ci = [
"unstable-msc3575",
"unstable-msc3618",
"unstable-msc3723",
"unstable-msc3814",
"unstable-msc3927",
"unstable-msc3932",
"unstable-msc3954",
Expand Down

0 comments on commit 0a82459

Please sign in to comment.