Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/matrix-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ All notable changes to this project will be documented in this file.

### Features

- Support the stable `m.key_backup` prefix for MSC4287: Sharing key backup
preference between clients.
([#6410](https://github.com/matrix-org/matrix-rust-sdk/pull/6410))
- [**breaking**] Added `HomeserverCapabilities` and `Client::homeserver_capabilities()` to get the capabilities
of the homeserver. This replaces `Client::get_capabilities()`.
of the homeserver. This replaces `Client::get_capabilities()`.
([#6371](https://github.com/matrix-org/matrix-rust-sdk/pull/6371))
- [**breaking**] `matrix_sdk::error::Error` has a new variant `Timeout` which occurs when
a cross-signing reset does not succeed after some period of time.
Expand Down
28 changes: 20 additions & 8 deletions crates/matrix-sdk/src/encryption/recovery/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ mod types;
pub use self::types::{EnableProgress, RecoveryError, RecoveryState, Result};
use self::{
futures::{Enable, RecoverAndReset, Reset},
types::{BackupDisabledContent, SecretStorageDisabledContent},
types::{BackupDisabledContent, KeyBackupContent, SecretStorageDisabledContent},
};
use crate::encryption::{AuthData, CrossSigningResetAuthType, CrossSigningResetHandle};

Expand Down Expand Up @@ -325,6 +325,8 @@ impl Recovery {
// Now let's "delete" the actual `m.secret.storage.default_key` event.
self.client.account().set_account_data(SecretStorageDisabledContent {}).await?;
// Make sure that we don't re-enable backups automatically.
self.client.account().set_account_data(KeyBackupContent { enabled: false }).await?;
// (Unstable prefix version of KeyBackupContent)
self.client.account().set_account_data(BackupDisabledContent { disabled: true }).await?;
// Finally, "delete" all the known secrets we have in the account data.
self.delete_all_known_secrets().await?;
Expand Down Expand Up @@ -651,16 +653,26 @@ impl Recovery {
/// Run a network request to figure whether backups have been disabled at
/// the account level.
async fn are_backups_marked_as_disabled(&self) -> Result<bool> {
Ok(self
.client
.account()
.fetch_account_data_static::<BackupDisabledContent>()
.await?
.map(|event| event.deserialize().map(|event| event.disabled).unwrap_or(false))
.unwrap_or(false))
if let Some(key_backup_content) =
self.client.account().fetch_account_data_static::<KeyBackupContent>().await?
{
Ok(key_backup_content.deserialize().map(|event| !event.enabled).unwrap_or(false))
} else {
Ok(self
.client
.account()
.fetch_account_data_static::<BackupDisabledContent>()
.await?
.map(|event| event.deserialize().map(|event| event.disabled).unwrap_or(false))
.unwrap_or(false))
}
}

async fn mark_backup_as_enabled(&self) -> Result<()> {
self.client.account().set_account_data(KeyBackupContent { enabled: true }).await?;

// Unstable prefix: will be removed when sufficient time has passed for clients
// to use the stable prefix.
self.client.account().set_account_data(BackupDisabledContent { disabled: false }).await?;

Ok(())
Expand Down
13 changes: 11 additions & 2 deletions crates/matrix-sdk/src/encryption/recovery/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,22 @@ pub enum RecoveryState {
#[ruma_event(type = "m.secret_storage.default_key", kind = GlobalAccountData)]
pub(super) struct SecretStorageDisabledContent {}

/// A custom global account data event which tells us that a new backup should
/// not be automatically created.
/// A global account data event which tells us that a new backup should
/// be automatically created.
///
/// This event is defined in [MSC4287].
///
/// [MSC4287]: https://github.com/matrix-org/matrix-spec-proposals/pull/4287
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "m.key_backup", kind = GlobalAccountData)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the stable type should now live in Ruma.

pub(super) struct KeyBackupContent {
pub enabled: bool,
}

/// Unstable prefix form of [MSC4287].
///
/// [MSC4287]: https://github.com/matrix-org/matrix-spec-proposals/pull/4287
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep this one around for a while.

#[ruma_event(type = "m.org.matrix.custom.backup_disabled", kind = GlobalAccountData)]
pub(super) struct BackupDisabledContent {
pub disabled: bool,
Expand Down
85 changes: 75 additions & 10 deletions crates/matrix-sdk/tests/integration/encryption/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,26 @@ async fn enable(
) {
let recovery = client.encryption().recovery();

let key_backup_content = Arc::new(Mutex::new(None));

let _quard = Mock::given(method("PUT"))
.and(path(format!("_matrix/client/r0/user/{user_id}/account_data/m.key_backup")))
.and(header("authorization", "Bearer 1234"))
.and({
let key_backup_content = key_backup_content.clone();
move |request: &wiremock::Request| {
let content: Value = request.body_json().expect("The body should be a JSON body");

*key_backup_content.lock().unwrap() = Some(content);

true
}
})
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.expect(1)
.mount_as_scoped(server)
.await;

let backup_disabled_content = Arc::new(Mutex::new(None));

let _quard = Mock::given(method("PUT"))
Expand Down Expand Up @@ -493,6 +513,26 @@ async fn test_backups_enabling() {
.mount(&server)
.await;

Mock::given(method("PUT"))
.and(path(format!("_matrix/client/r0/user/{user_id}/account_data/m.key_backup")))
.and(header("authorization", "Bearer 1234"))
.and(|request: &wiremock::Request| {
#[derive(Deserialize)]
struct Enabled {
enabled: bool,
}

let content: Enabled = request.body_json().expect("The body should be a JSON body");

assert!(content.enabled, "The backup support should be marked as enabled.");

true
})
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.expect(1)
.mount(&server)
.await;

Mock::given(method("PUT"))
.and(path(format!(
"_matrix/client/r0/user/{user_id}/account_data/m.org.matrix.custom.backup_disabled"
Expand Down Expand Up @@ -612,6 +652,26 @@ async fn test_recovery_disabling() {
.mount(&server)
.await;

Mock::given(method("PUT"))
.and(path(format!("_matrix/client/r0/user/{user_id}/account_data/m.key_backup")))
.and(header("authorization", "Bearer 1234"))
.and(|request: &wiremock::Request| {
#[derive(Deserialize)]
struct Enabled {
enabled: bool,
}

let content: Enabled = request.body_json().expect("The body should be a JSON body");

assert!(!content.enabled, "The backup support should be marked as disabled.");

true
})
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.expect(1)
.mount(&server)
.await;

Mock::given(method("PUT"))
.and(path(format!(
"_matrix/client/r0/user/{user_id}/account_data/m.org.matrix.custom.backup_disabled"
Expand Down Expand Up @@ -907,6 +967,15 @@ async fn test_reset_identity() {
.await;

// Re-enable backups
Mock::given(method("PUT"))
.and(path(format!("_matrix/client/r0/user/{user_id}/account_data/m.key_backup")))
.and(header("authorization", "Bearer 1234"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.expect(1)
.named("m.key_backup PUT")
.mount(&server)
.await;

Mock::given(method("PUT"))
.and(path(format!(
"_matrix/client/r0/user/{user_id}/account_data/m.org.matrix.custom.backup_disabled"
Expand All @@ -919,18 +988,14 @@ async fn test_reset_identity() {
.await;

Mock::given(method("GET"))
.and(path(format!(
"_matrix/client/r0/user/{user_id}/account_data/m.org.matrix.custom.backup_disabled"
)))
.and(path(format!("_matrix/client/r0/user/{user_id}/account_data/m.key_backup")))
.and(header("authorization", "Bearer 1234"))
.respond_with(ResponseTemplate::new(200).set_body_json(
json!({"type": "m.org.matrix.custom.backup_disabled",
"content": {
"disabled": false
}}),
))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"type": "m.key_backup",
"content": {
"enabled": true
}})))
.expect(1)
.named("m.org.matrix.custom.backup_disabled GET")
.named("m.key_backup GET")
.mount(&server)
.await;

Expand Down
Loading