Skip to main content

tuwunel_api_client/device/
devices_device.rs

1use axum::extract::State;
2use ruma::{
3	MilliSecondsSinceUnixEpoch,
4	api::client::device::{
5		Device, delete_device, get_device,
6		update_device::{self, v3::Response as UpdateDeviceResponse},
7	},
8};
9use tuwunel_core::{Err, Result, debug, err, utils::string::to_small_string};
10
11use crate::{ClientIp, Ruma, router::auth_uiaa};
12
13/// # `GET /_matrix/client/r0/devices/{deviceId}`
14///
15/// Get metadata on a single device of the sender user.
16pub(crate) async fn get_device_route(
17	State(services): State<crate::State>,
18	body: Ruma<get_device::v3::Request>,
19) -> Result<get_device::v3::Response> {
20	let device = services
21		.users
22		.get_device_metadata(body.sender_user(), &body.body.device_id)
23		.await
24		.map_err(|_| err!(Request(NotFound("Device not found."))))?;
25
26	Ok(get_device::v3::Response { device })
27}
28
29/// # `PUT /_matrix/client/r0/devices/{deviceId}`
30///
31/// Updates the metadata on a given device of the sender user, or creates the
32/// device when called by an appservice with MSC4190 enabled, returning 201 on
33/// creation and 200 on update.
34#[tracing::instrument(skip_all, fields(%client), name = "update_device")]
35pub(crate) async fn update_device_route(
36	State(services): State<crate::State>,
37	ClientIp(client): ClientIp,
38	body: Ruma<update_device::v3::Request>,
39) -> Result<UpdateDeviceResponse> {
40	let sender_user = body.sender_user();
41	let appservice = body.appservice_info.as_ref();
42	let update = |device: Device| {
43		// An omitted name leaves the device's own in place.
44		let renamed = body
45			.display_name
46			.clone()
47			.filter(|name| device.display_name.as_ref() != Some(name));
48
49		let notify = renamed.is_some();
50		let device = Device {
51			display_name: renamed.or(device.display_name),
52			last_seen_ip: Some(to_small_string(client)),
53			last_seen_ts: Some(MilliSecondsSinceUnixEpoch::now()),
54			..device
55		};
56
57		(device, notify)
58	};
59
60	match services
61		.users
62		.update_device_metadata(sender_user, &body.device_id, update)
63		.await
64	{
65		| Ok(()) => Ok(UpdateDeviceResponse::new()),
66		| Err(_) => {
67			let Some(appservice) = appservice else {
68				return Err!(Request(NotFound("Device not found.")));
69			};
70
71			if !appservice.registration.device_management {
72				return Err!(Request(NotFound("Device management not enabled for appservice.")));
73			}
74
75			debug!(
76				"Creating new device for {sender_user} from appservice {} as MSC4190 is enabled \
77				 and device ID does not exist",
78				appservice.registration.id
79			);
80
81			services
82				.users
83				.create_device(
84					sender_user,
85					Some(&body.device_id),
86					(None, None),
87					None,
88					body.display_name.as_deref(),
89					Some(client),
90				)
91				.await?;
92
93			Ok(UpdateDeviceResponse::created())
94		},
95	}
96}
97
98/// # `DELETE /_matrix/client/r0/devices/{deviceId}`
99///
100/// Deletes the given device.
101///
102/// - Requires UIAA to verify user password
103/// - Invalidates access token
104/// - Deletes device metadata (device id, device display name, last seen ip,
105///   last seen ts)
106/// - Forgets to-device events
107/// - Triggers device list updates
108pub(crate) async fn delete_device_route(
109	State(services): State<crate::State>,
110	body: Ruma<delete_device::v3::Request>,
111) -> Result<delete_device::v3::Response> {
112	let appservice = body.appservice_info.as_ref();
113
114	if appservice.is_some_and(|appservice| appservice.registration.device_management) {
115		let sender_user = body.sender_user();
116		debug!(
117			"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
118			 enabled"
119		);
120		services
121			.users
122			.remove_device(sender_user, &body.device_id)
123			.await;
124
125		return Ok(delete_device::v3::Response {});
126	}
127
128	let ref sender_user = auth_uiaa(&services, &body).await?;
129
130	services
131		.users
132		.remove_device(sender_user, &body.device_id)
133		.await;
134
135	Ok(delete_device::v3::Response {})
136}