tuwunel_api_client/device/
devices_device.rs1use 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
13pub(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#[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 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
98pub(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}