Skip to main content

tuwunel_api/client/device/
devices_device.rs

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