tuwunel_api/server/
user.rs1use axum::extract::State;
2use futures::{FutureExt, StreamExt, TryFutureExt, future::join5};
3use ruma::{
4 UserId,
5 api::{
6 client::device::Device,
7 federation::{
8 device::get_devices::{self, v1::UserDevice},
9 keys::{claim_keys, get_keys},
10 },
11 },
12 uint,
13};
14use tuwunel_core::{Err, Result, utils::future::TryExtExt};
15
16use crate::{
17 Ruma,
18 client::{claim_keys_helper, get_keys_helper},
19};
20
21pub(crate) async fn get_devices_route(
25 State(services): State<crate::State>,
26 body: Ruma<get_devices::v1::Request>,
27) -> Result<get_devices::v1::Response> {
28 let user_id = &body.user_id;
29 if !services.globals.user_is_local(user_id) {
30 return Err!(Request(InvalidParam("Tried to access user from other server.")));
31 }
32
33 let allowed_signatures = |u: &UserId| u.server_name() == body.origin();
34
35 let master_key = services
36 .users
37 .get_master_key(None, user_id, &allowed_signatures)
38 .ok();
39
40 let self_signing_key = services
41 .users
42 .get_self_signing_key(None, user_id, &allowed_signatures)
43 .ok();
44
45 let stream_id = services
46 .users
47 .get_devicelist_version(user_id)
48 .map_ok(TryInto::try_into)
49 .map_ok(Result::ok)
50 .ok();
51
52 let devices = services
53 .users
54 .all_devices_metadata(user_id)
55 .filter_map(async |Device { device_id, display_name, .. }: Device| {
56 let device_display_name = services
57 .config
58 .allow_device_name_federation
59 .then_some(display_name)
60 .flatten()
61 .or_else(|| Some(device_id.as_str().into()));
62
63 services
64 .users
65 .get_device_keys(user_id, &device_id)
66 .map_ok(|keys| UserDevice {
67 device_id: device_id.clone(),
68 device_display_name,
69 keys,
70 })
71 .map(Result::ok)
72 .await
73 })
74 .collect::<Vec<_>>();
75
76 let appservice_keys = services.appservice.query_keys(user_id, &[]);
77
78 let (stream_id, master_key, self_signing_key, mut devices, appservice_keys) =
79 join5(stream_id, master_key, self_signing_key, devices, appservice_keys)
80 .boxed()
81 .await;
82
83 if !appservice_keys.is_empty() {
84 devices.retain(|device| !appservice_keys.contains_key(&device.device_id));
85 devices.extend(
86 appservice_keys
87 .into_iter()
88 .map(|(device_id, keys)| UserDevice {
89 device_id,
90 keys,
91 device_display_name: None,
92 }),
93 );
94 }
95
96 Ok(get_devices::v1::Response {
97 user_id: body.body.user_id,
98 stream_id: stream_id.flatten().unwrap_or_else(|| uint!(0)),
99 devices,
100 self_signing_key,
101 master_key,
102 })
103}
104
105pub(crate) async fn get_keys_route(
109 State(services): State<crate::State>,
110 body: Ruma<get_keys::v1::Request>,
111) -> Result<get_keys::v1::Response> {
112 if body
113 .device_keys
114 .iter()
115 .any(|(u, _)| !services.globals.user_is_local(u))
116 {
117 return Err!(Request(InvalidParam("User does not belong to this server.")));
118 }
119
120 let result = get_keys_helper(
121 &services,
122 None,
123 &body.device_keys,
124 |u| Some(u.server_name()) == body.origin.as_deref(),
125 services.config.allow_device_name_federation,
126 )
127 .await?;
128
129 Ok(get_keys::v1::Response {
130 device_keys: result.device_keys,
131 master_keys: result.master_keys,
132 self_signing_keys: result.self_signing_keys,
133 })
134}
135
136pub(crate) async fn claim_keys_route(
140 State(services): State<crate::State>,
141 body: Ruma<claim_keys::v1::Request>,
142) -> Result<claim_keys::v1::Response> {
143 if body
144 .one_time_keys
145 .iter()
146 .any(|(u, _)| !services.globals.user_is_local(u))
147 {
148 return Err!(Request(InvalidParam("Tried to access user from other server.")));
149 }
150
151 let result = claim_keys_helper(&services, &body.one_time_keys).await?;
152
153 Ok(claim_keys::v1::Response { one_time_keys: result.one_time_keys })
154}