263 lines
6.8 KiB
Rust
263 lines
6.8 KiB
Rust
use axum::extract::State;
|
|
use axum_client_ip::InsecureClientIp;
|
|
use conduwuit::{Err, Error, Result, debug, err, utils};
|
|
use futures::StreamExt;
|
|
use ruma::{
|
|
MilliSecondsSinceUnixEpoch, OwnedDeviceId,
|
|
api::client::{
|
|
device::{self, delete_device, delete_devices, get_device, get_devices, update_device},
|
|
error::ErrorKind,
|
|
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
|
},
|
|
};
|
|
|
|
use super::SESSION_ID_LENGTH;
|
|
use crate::{Ruma, client::DEVICE_ID_LENGTH};
|
|
|
|
/// # `GET /_matrix/client/r0/devices`
|
|
///
|
|
/// Get metadata on all devices of the sender user.
|
|
pub(crate) async fn get_devices_route(
|
|
State(services): State<crate::State>,
|
|
body: Ruma<get_devices::v3::Request>,
|
|
) -> Result<get_devices::v3::Response> {
|
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
|
|
|
let devices: Vec<device::Device> = services
|
|
.users
|
|
.all_devices_metadata(sender_user)
|
|
.collect()
|
|
.await;
|
|
|
|
Ok(get_devices::v3::Response { devices })
|
|
}
|
|
|
|
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
|
///
|
|
/// Get metadata on a single device of the sender user.
|
|
pub(crate) async fn get_device_route(
|
|
State(services): State<crate::State>,
|
|
body: Ruma<get_device::v3::Request>,
|
|
) -> Result<get_device::v3::Response> {
|
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
|
|
|
let device = services
|
|
.users
|
|
.get_device_metadata(sender_user, &body.body.device_id)
|
|
.await
|
|
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
|
|
|
Ok(get_device::v3::Response { device })
|
|
}
|
|
|
|
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
|
///
|
|
/// Updates the metadata on a given device of the sender user.
|
|
#[tracing::instrument(skip_all, fields(%client), name = "update_device")]
|
|
pub(crate) async fn update_device_route(
|
|
State(services): State<crate::State>,
|
|
InsecureClientIp(client): InsecureClientIp,
|
|
body: Ruma<update_device::v3::Request>,
|
|
) -> Result<update_device::v3::Response> {
|
|
let sender_user = body.sender_user();
|
|
let appservice = body.appservice_info.as_ref();
|
|
|
|
match services
|
|
.users
|
|
.get_device_metadata(sender_user, &body.device_id)
|
|
.await
|
|
{
|
|
| Ok(mut device) => {
|
|
device.display_name.clone_from(&body.display_name);
|
|
device.last_seen_ip.clone_from(&Some(client.to_string()));
|
|
device
|
|
.last_seen_ts
|
|
.clone_from(&Some(MilliSecondsSinceUnixEpoch::now()));
|
|
|
|
services
|
|
.users
|
|
.update_device_metadata(sender_user, &body.device_id, &device)
|
|
.await?;
|
|
|
|
Ok(update_device::v3::Response {})
|
|
},
|
|
| Err(_) => {
|
|
let Some(appservice) = appservice else {
|
|
return Err!(Request(NotFound("Device not found.")));
|
|
};
|
|
if !appservice.registration.device_management {
|
|
return Err!(Request(NotFound("Device not found.")));
|
|
}
|
|
|
|
debug!(
|
|
"Creating new device for {sender_user} from appservice {} as MSC4190 is enabled \
|
|
and device ID does not exist",
|
|
appservice.registration.id
|
|
);
|
|
|
|
let device_id = OwnedDeviceId::from(utils::random_string(DEVICE_ID_LENGTH));
|
|
|
|
services
|
|
.users
|
|
.create_device(
|
|
sender_user,
|
|
&device_id,
|
|
&appservice.registration.as_token,
|
|
None,
|
|
Some(client.to_string()),
|
|
)
|
|
.await?;
|
|
|
|
return Ok(update_device::v3::Response {});
|
|
},
|
|
}
|
|
}
|
|
|
|
/// # `DELETE /_matrix/client/r0/devices/{deviceId}`
|
|
///
|
|
/// Deletes the given device.
|
|
///
|
|
/// - Requires UIAA to verify user password
|
|
/// - Invalidates access token
|
|
/// - Deletes device metadata (device id, device display name, last seen ip,
|
|
/// last seen ts)
|
|
/// - Forgets to-device events
|
|
/// - Triggers device list updates
|
|
pub(crate) async fn delete_device_route(
|
|
State(services): State<crate::State>,
|
|
body: Ruma<delete_device::v3::Request>,
|
|
) -> Result<delete_device::v3::Response> {
|
|
let (sender_user, sender_device) = body.sender();
|
|
let appservice = body.appservice_info.as_ref();
|
|
|
|
if appservice.is_some_and(|appservice| appservice.registration.device_management) {
|
|
debug!(
|
|
"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
|
|
enabled"
|
|
);
|
|
services
|
|
.users
|
|
.remove_device(sender_user, &body.device_id)
|
|
.await;
|
|
|
|
return Ok(delete_device::v3::Response {});
|
|
}
|
|
|
|
// UIAA
|
|
let mut uiaainfo = UiaaInfo {
|
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
|
completed: Vec::new(),
|
|
params: Box::default(),
|
|
session: None,
|
|
auth_error: None,
|
|
};
|
|
|
|
match &body.auth {
|
|
| Some(auth) => {
|
|
let (worked, uiaainfo) = services
|
|
.uiaa
|
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
|
.await?;
|
|
|
|
if !worked {
|
|
return Err!(Uiaa(uiaainfo));
|
|
}
|
|
// Success!
|
|
},
|
|
| _ => match body.json_body {
|
|
| Some(ref json) => {
|
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
|
services
|
|
.uiaa
|
|
.create(sender_user, sender_device, &uiaainfo, json);
|
|
|
|
return Err!(Uiaa(uiaainfo));
|
|
},
|
|
| _ => {
|
|
return Err!(Request(NotJson("Not json.")));
|
|
},
|
|
},
|
|
}
|
|
|
|
services
|
|
.users
|
|
.remove_device(sender_user, &body.device_id)
|
|
.await;
|
|
|
|
Ok(delete_device::v3::Response {})
|
|
}
|
|
|
|
/// # `POST /_matrix/client/v3/delete_devices`
|
|
///
|
|
/// Deletes the given list of devices.
|
|
///
|
|
/// - Requires UIAA to verify user password unless from an appservice with
|
|
/// MSC4190 enabled.
|
|
///
|
|
/// For each device:
|
|
/// - Invalidates access token
|
|
/// - Deletes device metadata (device id, device display name, last seen ip,
|
|
/// last seen ts)
|
|
/// - Forgets to-device events
|
|
/// - Triggers device list updates
|
|
pub(crate) async fn delete_devices_route(
|
|
State(services): State<crate::State>,
|
|
body: Ruma<delete_devices::v3::Request>,
|
|
) -> Result<delete_devices::v3::Response> {
|
|
let (sender_user, sender_device) = body.sender();
|
|
let appservice = body.appservice_info.as_ref();
|
|
|
|
if appservice.is_some_and(|appservice| appservice.registration.device_management) {
|
|
debug!(
|
|
"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
|
|
enabled"
|
|
);
|
|
for device_id in &body.devices {
|
|
services.users.remove_device(sender_user, device_id).await;
|
|
}
|
|
|
|
return Ok(delete_devices::v3::Response {});
|
|
}
|
|
|
|
// UIAA
|
|
let mut uiaainfo = UiaaInfo {
|
|
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
|
completed: Vec::new(),
|
|
params: Box::default(),
|
|
session: None,
|
|
auth_error: None,
|
|
};
|
|
|
|
match &body.auth {
|
|
| Some(auth) => {
|
|
let (worked, uiaainfo) = services
|
|
.uiaa
|
|
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
|
.await?;
|
|
|
|
if !worked {
|
|
return Err(Error::Uiaa(uiaainfo));
|
|
}
|
|
// Success!
|
|
},
|
|
| _ => match body.json_body {
|
|
| Some(ref json) => {
|
|
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
|
services
|
|
.uiaa
|
|
.create(sender_user, sender_device, &uiaainfo, json);
|
|
|
|
return Err(Error::Uiaa(uiaainfo));
|
|
},
|
|
| _ => {
|
|
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
|
},
|
|
},
|
|
}
|
|
|
|
for device_id in &body.devices {
|
|
services.users.remove_device(sender_user, device_id).await;
|
|
}
|
|
|
|
Ok(delete_devices::v3::Response {})
|
|
}
|