matrix-sdk: Add support to delete devices.
parent
425a07d670
commit
b1c8c64205
|
@ -65,7 +65,7 @@ pub enum LoopCtrl {
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::{
|
api::r0::{
|
||||||
account::register,
|
account::register,
|
||||||
device::get_devices,
|
device::{delete_devices, get_devices},
|
||||||
directory::{get_public_rooms, get_public_rooms_filtered},
|
directory::{get_public_rooms, get_public_rooms_filtered},
|
||||||
media::create_content,
|
media::create_content,
|
||||||
membership::{
|
membership::{
|
||||||
|
@ -82,6 +82,7 @@ use matrix_sdk_common::{
|
||||||
typing::create_typing_event::{
|
typing::create_typing_event::{
|
||||||
Request as TypingRequest, Response as TypingResponse, Typing,
|
Request as TypingRequest, Response as TypingResponse, Typing,
|
||||||
},
|
},
|
||||||
|
uiaa::AuthData,
|
||||||
},
|
},
|
||||||
assign,
|
assign,
|
||||||
events::{
|
events::{
|
||||||
|
@ -1383,6 +1384,71 @@ impl Client {
|
||||||
self.send(request).await
|
self.send(request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete the given devices from the server.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `devices` - The list of devices that should be deleted from the
|
||||||
|
/// server.
|
||||||
|
///
|
||||||
|
/// * `auth_data` - This request requires user interactive auth, the first
|
||||||
|
/// request needs to set this to `None` and will always fail with an
|
||||||
|
/// `UiaaResponse`. The response will contain information for the
|
||||||
|
/// interactive auth and the same request needs to be made but this time
|
||||||
|
/// with some `auth_data` provided.
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use matrix_sdk::{
|
||||||
|
/// # api::r0::uiaa::{UiaaResponse, AuthData},
|
||||||
|
/// # Client, SyncSettings, Error, FromHttpResponseError, ServerError,
|
||||||
|
/// # };
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # use serde_json::json;
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # use std::{collections::BTreeMap, convert::TryFrom};
|
||||||
|
/// # block_on(async {
|
||||||
|
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
|
||||||
|
/// # let mut client = Client::new(homeserver).unwrap();
|
||||||
|
/// let devices = &["DEVICEID".into()];
|
||||||
|
///
|
||||||
|
/// if let Err(e) = client.delete_devices(devices, None).await {
|
||||||
|
/// if let Some(info) = e.uiaa_response() {
|
||||||
|
/// let mut auth_parameters = BTreeMap::new();
|
||||||
|
///
|
||||||
|
/// let identifier = json!({
|
||||||
|
/// "type": "m.id.user",
|
||||||
|
/// "user": "example",
|
||||||
|
/// });
|
||||||
|
/// auth_parameters.insert("identifier".to_owned(), identifier);
|
||||||
|
/// auth_parameters.insert("password".to_owned(), "wordpass".into());
|
||||||
|
///
|
||||||
|
/// // This is needed because of https://github.com/matrix-org/synapse/issues/5665
|
||||||
|
/// auth_parameters.insert("user".to_owned(), "@example:localhost".into());
|
||||||
|
///
|
||||||
|
/// let auth_data = AuthData::DirectRequest {
|
||||||
|
/// kind: "m.login.password",
|
||||||
|
/// auth_parameters,
|
||||||
|
/// session: info.session.as_deref(),
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// client
|
||||||
|
/// .delete_devices(devices, Some(auth_data))
|
||||||
|
/// .await
|
||||||
|
/// .expect("Can't delete devices");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// # });
|
||||||
|
pub async fn delete_devices(
|
||||||
|
&self,
|
||||||
|
devices: &[DeviceIdBox],
|
||||||
|
auth_data: Option<AuthData<'_>>,
|
||||||
|
) -> Result<delete_devices::Response> {
|
||||||
|
let mut request = delete_devices::Request::new(devices);
|
||||||
|
request.auth = auth_data;
|
||||||
|
|
||||||
|
self.send(request).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Synchronize the client's state with the latest state on the server.
|
/// Synchronize the client's state with the latest state on the server.
|
||||||
///
|
///
|
||||||
/// **Note**: You should not use this method to repeatedly sync if encryption
|
/// **Note**: You should not use this method to repeatedly sync if encryption
|
||||||
|
@ -1956,7 +2022,10 @@ mod test {
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use std::{convert::TryInto, io::Cursor, path::Path, str::FromStr, time::Duration};
|
use std::{
|
||||||
|
collections::BTreeMap, convert::TryInto, io::Cursor, path::Path, str::FromStr,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
async fn logged_in_client() -> Client {
|
async fn logged_in_client() -> Client {
|
||||||
let session = Session {
|
let session = Session {
|
||||||
|
@ -2755,4 +2824,61 @@ mod test {
|
||||||
|
|
||||||
assert_eq!("tutorial".to_string(), room.read().await.display_name());
|
assert_eq!("tutorial".to_string(), room.read().await.display_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn delete_devices() {
|
||||||
|
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
|
||||||
|
let client = Client::new(homeserver).unwrap();
|
||||||
|
|
||||||
|
let _m = mock("POST", "/_matrix/client/r0/delete_devices")
|
||||||
|
.with_status(401)
|
||||||
|
.with_body(
|
||||||
|
json!({
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"stages": [
|
||||||
|
"m.login.password"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": {},
|
||||||
|
"session": "vBslorikviAjxzYBASOBGfPp"
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let _m = mock("POST", "/_matrix/client/r0/delete_devices")
|
||||||
|
.with_status(401)
|
||||||
|
// empty response
|
||||||
|
// TODO rename that response type.
|
||||||
|
.with_body(test_json::LOGOUT.to_string())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let devices = &["DEVICEID".into()];
|
||||||
|
|
||||||
|
if let Err(e) = client.delete_devices(devices, None).await {
|
||||||
|
if let Some(info) = e.uiaa_response() {
|
||||||
|
let mut auth_parameters = BTreeMap::new();
|
||||||
|
|
||||||
|
let identifier = json!({
|
||||||
|
"type": "m.id.user",
|
||||||
|
"user": "example",
|
||||||
|
});
|
||||||
|
auth_parameters.insert("identifier".to_owned(), identifier);
|
||||||
|
auth_parameters.insert("password".to_owned(), "wordpass".into());
|
||||||
|
|
||||||
|
let auth_data = AuthData::DirectRequest {
|
||||||
|
kind: "m.login.password",
|
||||||
|
auth_parameters,
|
||||||
|
session: info.session.as_deref(),
|
||||||
|
};
|
||||||
|
|
||||||
|
client
|
||||||
|
.delete_devices(devices, Some(auth_data))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,11 @@
|
||||||
|
|
||||||
use matrix_sdk_base::Error as MatrixError;
|
use matrix_sdk_base::Error as MatrixError;
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::{r0::uiaa::UiaaResponse as UiaaError, Error as RumaClientError},
|
api::{
|
||||||
FromHttpResponseError as RumaResponseError, IntoHttpError as RumaIntoHttpError,
|
r0::uiaa::{UiaaInfo, UiaaResponse as UiaaError},
|
||||||
|
Error as RumaClientError,
|
||||||
|
},
|
||||||
|
FromHttpResponseError as RumaResponseError, IntoHttpError as RumaIntoHttpError, ServerError,
|
||||||
};
|
};
|
||||||
use reqwest::Error as ReqwestError;
|
use reqwest::Error as ReqwestError;
|
||||||
use serde_json::Error as JsonError;
|
use serde_json::Error as JsonError;
|
||||||
|
@ -79,6 +82,30 @@ pub enum Error {
|
||||||
UiaaError(RumaResponseError<UiaaError>),
|
UiaaError(RumaResponseError<UiaaError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
/// Try to destructure the error into an universal interactive auth info.
|
||||||
|
///
|
||||||
|
/// Some requests require universal interactive auth, doing such a request
|
||||||
|
/// will always fail the first time with a 401 status code, the response
|
||||||
|
/// body will contain info how the client can authenticate.
|
||||||
|
///
|
||||||
|
/// The request will need to be retried, this time containing additional
|
||||||
|
/// authentication data.
|
||||||
|
///
|
||||||
|
/// This method is an convenience method to get to the info the server
|
||||||
|
/// returned on the first, failed request.
|
||||||
|
pub fn uiaa_response(&self) -> Option<&UiaaInfo> {
|
||||||
|
if let Error::UiaaError(RumaResponseError::Http(ServerError::Known(
|
||||||
|
UiaaError::AuthResponse(i),
|
||||||
|
))) = self
|
||||||
|
{
|
||||||
|
Some(i)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<RumaResponseError<UiaaError>> for Error {
|
impl From<RumaResponseError<UiaaError>> for Error {
|
||||||
fn from(error: RumaResponseError<UiaaError>) -> Self {
|
fn from(error: RumaResponseError<UiaaError>) -> Self {
|
||||||
Self::UiaaError(error)
|
Self::UiaaError(error)
|
||||||
|
|
Loading…
Reference in New Issue