From 5957112ff6ce4b46be4784d5a4648fc37697b823 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Wed, 22 Nov 2023 18:22:57 +0100 Subject: [PATCH] Interface to allow cross-signing reset using Synapse admin API --- crates/matrix-synapse/src/lib.rs | 36 ++++++++++++++++++++++++++++++++ crates/matrix/src/lib.rs | 16 ++++++++++++++ crates/matrix/src/mock.rs | 9 ++++++++ 3 files changed, 61 insertions(+) diff --git a/crates/matrix-synapse/src/lib.rs b/crates/matrix-synapse/src/lib.rs index 0bd2d802..fb3b5f08 100644 --- a/crates/matrix-synapse/src/lib.rs +++ b/crates/matrix-synapse/src/lib.rs @@ -130,6 +130,9 @@ struct SynapseDeactivateUserRequest { erase: bool, } +#[derive(Serialize)] +struct SynapseAllowCrossSigningResetRequest {} + #[async_trait::async_trait] impl HomeserverConnection for SynapseConnection { type Error = anyhow::Error; @@ -366,4 +369,37 @@ impl HomeserverConnection for SynapseConnection { async fn unset_displayname(&self, mxid: &str) -> Result<(), Self::Error> { self.set_displayname(mxid, "").await } + + #[tracing::instrument( + name = "homeserver.allow_cross_signing_reset", + skip_all, + fields( + matrix.homeserver = self.homeserver, + matrix.mxid = mxid, + ), + err(Display), + )] + async fn allow_cross_signing_reset(&self, mxid: &str) -> Result<(), Self::Error> { + let mut client = self + .http_client_factory + .client("homeserver.allow_cross_signing_reset") + .request_bytes_to_body() + .json_request(); + + let request = self + .post(&format!( + "_synapse/admin/v1/users/{mxid}/_allow_cross_signing_replacement_without_uia" + )) + .body(SynapseAllowCrossSigningResetRequest {})?; + + let response = client.ready().await?.call(request).await?; + + if response.status() != StatusCode::OK { + return Err(anyhow::anyhow!( + "Failed to allow cross signing reset in Synapse" + )); + } + + Ok(()) + } } diff --git a/crates/matrix/src/lib.rs b/crates/matrix/src/lib.rs index 14f163d7..6953ef3d 100644 --- a/crates/matrix/src/lib.rs +++ b/crates/matrix/src/lib.rs @@ -282,6 +282,18 @@ pub trait HomeserverConnection: Send + Sync { /// Returns an error if the homeserver is unreachable or the displayname /// could not be unset. async fn unset_displayname(&self, mxid: &str) -> Result<(), Self::Error>; + + /// Temporarily allow a user to reset their cross-signing keys. + /// + /// # Parameters + /// + /// * `mxid` - The Matrix ID of the user to allow cross-signing key reset + /// + /// # Errors + /// + /// Returns an error if the homeserver is unreachable or the cross-signing + /// reset could not be allowed. + async fn allow_cross_signing_reset(&self, mxid: &str) -> Result<(), Self::Error>; } #[async_trait::async_trait] @@ -319,4 +331,8 @@ impl HomeserverConnection for &T async fn unset_displayname(&self, mxid: &str) -> Result<(), Self::Error> { (**self).unset_displayname(mxid).await } + + async fn allow_cross_signing_reset(&self, mxid: &str) -> Result<(), Self::Error> { + (**self).allow_cross_signing_reset(mxid).await + } } diff --git a/crates/matrix/src/mock.rs b/crates/matrix/src/mock.rs index c9209334..d6e9ffe5 100644 --- a/crates/matrix/src/mock.rs +++ b/crates/matrix/src/mock.rs @@ -26,6 +26,7 @@ struct MockUser { displayname: Option, devices: HashSet, emails: Option>, + cross_signing_reset_allowed: bool, } /// A mock implementation of a [`HomeserverConnection`], which never fails and @@ -74,6 +75,7 @@ impl crate::HomeserverConnection for HomeserverConnection { displayname: None, devices: HashSet::new(), emails: None, + cross_signing_reset_allowed: false, }); anyhow::ensure!( @@ -136,6 +138,13 @@ impl crate::HomeserverConnection for HomeserverConnection { user.displayname = None; Ok(()) } + + async fn allow_cross_signing_reset(&self, mxid: &str) -> Result<(), Self::Error> { + let mut users = self.users.write().await; + let user = users.get_mut(mxid).context("User not found")?; + user.cross_signing_reset_allowed = true; + Ok(()) + } } #[cfg(test)]