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