Skip to main content

tuwunel_api_federation/
user.rs

1use 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
22/// # `GET /_matrix/federation/v1/user/devices/{userId}`
23///
24/// Gets information on all devices of the user.
25pub(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	// size firewall
79	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
92/// # `POST /_matrix/federation/v1/user/keys/query`
93///
94/// Gets devices and identity keys for the given users.
95pub(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
128/// # `POST /_matrix/federation/v1/user/keys/claim`
129///
130/// Claims one-time keys.
131pub(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}