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
62			Ok(update_device::v3::Response::new())
63		},
64		| Err(_) => {
65			let Some(appservice) = appservice else {
66				return Err!(Request(NotFound("Device not found.")));
67			};
68
69			if !appservice.registration.device_management {
70				return Err!(Request(NotFound("Device management not enabled for appservice.")));
71			}
72
73			debug!(
74				"Creating new device for {sender_user} from appservice {} as MSC4190 is enabled \
75				 and device ID does not exist",
76				appservice.registration.id
77			);
78
79			services
80				.users
81				.create_device(
82					sender_user,
83					Some(&body.device_id),
84					(None, None),
85					None,
86					body.display_name.as_deref(),
87					Some(client),
88				)
89				.await?;
90
91			Ok(update_device::v3::Response::created())
92		},
93	}
94}
95
96/// # `DELETE /_matrix/client/r0/devices/{deviceId}`
97///
98/// Deletes the given device.
99///
100/// - Requires UIAA to verify user password
101/// - Invalidates access token
102/// - Deletes device metadata (device id, device display name, last seen ip,
103///   last seen ts)
104/// - Forgets to-device events
105/// - Triggers device list updates
106pub(crate) async fn delete_device_route(
107	State(services): State<crate::State>,
108	body: Ruma<delete_device::v3::Request>,
109) -> Result<delete_device::v3::Response> {
110	let appservice = body.appservice_info.as_ref();
111
112	if appservice.is_some_and(|appservice| appservice.registration.device_management) {
113		let sender_user = body.sender_user();
114		debug!(
115			"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
116			 enabled"
117		);
118		services
119			.users
120			.remove_device(sender_user, &body.device_id)
121			.await;
122
123		return Ok(delete_device::v3::Response {});
124	}
125
126	let ref sender_user = auth_uiaa(&services, &body).await?;
127
128	services
129		.users
130		.remove_device(sender_user, &body.device_id)
131		.await;
132
133	Ok(delete_device::v3::Response {})
134}