tuwunel_api/client/
device.rs1use axum::extract::State;
2use futures::StreamExt;
3use ruma::{
4 MilliSecondsSinceUnixEpoch,
5 api::client::device::{
6 self, delete_device, delete_devices, get_device, get_devices, update_device,
7 },
8};
9use tuwunel_core::{Err, Result, debug, err, utils::string::to_small_string};
10
11use crate::{ClientIp, Ruma, router::auth_uiaa};
12
13pub(crate) async fn get_devices_route(
17 State(services): State<crate::State>,
18 body: Ruma<get_devices::v3::Request>,
19) -> Result<get_devices::v3::Response> {
20 let devices: Vec<device::Device> = services
21 .users
22 .all_devices_metadata(body.sender_user())
23 .collect()
24 .await;
25
26 Ok(get_devices::v3::Response { devices })
27}
28
29pub(crate) async fn get_device_route(
33 State(services): State<crate::State>,
34 body: Ruma<get_device::v3::Request>,
35) -> Result<get_device::v3::Response> {
36 let device = services
37 .users
38 .get_device_metadata(body.sender_user(), &body.body.device_id)
39 .await
40 .map_err(|_| err!(Request(NotFound("Device not found."))))?;
41
42 Ok(get_device::v3::Response { device })
43}
44
45#[tracing::instrument(skip_all, fields(%client), name = "update_device")]
49pub(crate) async fn update_device_route(
50 State(services): State<crate::State>,
51 ClientIp(client): ClientIp,
52 body: Ruma<update_device::v3::Request>,
53) -> Result<update_device::v3::Response> {
54 let sender_user = body.sender_user();
55 let appservice = body.appservice_info.as_ref();
56
57 match services
58 .users
59 .get_device_metadata(sender_user, &body.device_id)
60 .await
61 {
62 | Ok(mut device) => {
63 let notify = device.display_name != body.display_name;
64 device.display_name.clone_from(&body.display_name);
65
66 device
67 .last_seen_ip
68 .clone_from(&Some(to_small_string(client)));
69
70 device
71 .last_seen_ts
72 .clone_from(&Some(MilliSecondsSinceUnixEpoch::now()));
73
74 assert_eq!(device.device_id, body.device_id, "device_id mismatch");
75 services
76 .users
77 .put_device_metadata(sender_user, notify, &device);
78
79 Ok(update_device::v3::Response {})
80 },
81 | Err(_) => {
82 let Some(appservice) = appservice else {
83 return Err!(Request(NotFound("Device not found.")));
84 };
85
86 if !appservice.registration.device_management {
87 return Err!(Request(NotFound("Device management not enabled for appservice.")));
88 }
89
90 debug!(
91 "Creating new device for {sender_user} from appservice {} as MSC4190 is enabled \
92 and device ID does not exist",
93 appservice.registration.id
94 );
95
96 services
97 .users
98 .create_device(
99 sender_user,
100 Some(&body.device_id),
101 (None, None),
102 None,
103 body.display_name.as_deref(),
104 Some(client),
105 )
106 .await?;
107
108 return Ok(update_device::v3::Response {});
109 },
110 }
111}
112
113pub(crate) async fn delete_device_route(
124 State(services): State<crate::State>,
125 body: Ruma<delete_device::v3::Request>,
126) -> Result<delete_device::v3::Response> {
127 let appservice = body.appservice_info.as_ref();
128
129 if appservice.is_some_and(|appservice| appservice.registration.device_management) {
130 let sender_user = body.sender_user();
131 debug!(
132 "Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
133 enabled"
134 );
135 services
136 .users
137 .remove_device(sender_user, &body.device_id)
138 .await;
139
140 return Ok(delete_device::v3::Response {});
141 }
142
143 let ref sender_user = auth_uiaa(&services, &body).await?;
144
145 services
146 .users
147 .remove_device(sender_user, &body.device_id)
148 .await;
149
150 Ok(delete_device::v3::Response {})
151}
152
153pub(crate) async fn delete_devices_route(
167 State(services): State<crate::State>,
168 body: Ruma<delete_devices::v3::Request>,
169) -> Result<delete_devices::v3::Response> {
170 let appservice = body.appservice_info.as_ref();
171
172 if appservice.is_some_and(|appservice| appservice.registration.device_management) {
173 let sender_user = body.sender_user();
174 debug!(
175 "Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
176 enabled"
177 );
178 for device_id in &body.devices {
179 services
180 .users
181 .remove_device(sender_user, device_id)
182 .await;
183 }
184
185 return Ok(delete_devices::v3::Response {});
186 }
187
188 let ref sender_user = auth_uiaa(&services, &body).await?;
189
190 for device_id in &body.devices {
191 services
192 .users
193 .remove_device(sender_user, device_id)
194 .await;
195 }
196
197 Ok(delete_devices::v3::Response {})
198}