Skip to main content

tuwunel_api/server/
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_core::{Err, Result, utils::future::TryExtExt};
18
19use crate::{
20	Ruma,
21	client::{claim_keys_helper, get_keys_helper},
22};
23
24/// # `GET /_matrix/federation/v1/user/devices/{userId}`
25///
26/// Gets information on all devices of the user.
27pub(crate) async fn get_devices_route(
28	State(services): State<crate::State>,
29	body: Ruma<get_devices::v1::Request>,
30) -> Result<get_devices::v1::Response> {
31	let user_id = &body.user_id;
32	if !services.globals.user_is_local(user_id) {
33		return Err!(Request(InvalidParam("Tried to access user from other server.")));
34	}
35
36	let allowed_signatures = |u: &UserId| u.server_name() == body.origin();
37	let stream_id = services
38		.users
39		.get_devicelist_version(user_id)
40		.map_ok(TryInto::try_into)
41		.map_ok(Result::ok)
42		.ok()
43		.await;
44
45	let master_key = services
46		.users
47		.get_master_key(None, user_id, &allowed_signatures)
48		.ok();
49
50	let self_signing_key = services
51		.users
52		.get_self_signing_key(None, user_id, &allowed_signatures)
53		.ok();
54
55	let devices = services
56		.users
57		.all_devices_metadata(user_id)
58		.filter_map(async |Device { device_id, display_name, .. }: Device| {
59			let device_display_name = services
60				.config
61				.allow_device_name_federation
62				.then_some(display_name)
63				.flatten()
64				.or_else(|| Some(device_id.as_str().into()));
65
66			services
67				.users
68				.get_device_keys(user_id, &device_id)
69				.map_ok(|keys| UserDevice {
70					device_id: device_id.clone(),
71					device_display_name,
72					keys,
73				})
74				.map(Result::ok)
75				.await
76		})
77		.collect::<Vec<_>>();
78
79	// size firewall
80	let (master_key, self_signing_key, devices) = join3(master_key, self_signing_key, devices)
81		.boxed()
82		.await;
83
84	Ok(GetDevicesResponse {
85		user_id: body.body.user_id,
86		stream_id: stream_id.flatten().unwrap_or_else(|| uint!(0)),
87		devices,
88		self_signing_key,
89		master_key,
90	})
91}
92
93/// # `POST /_matrix/federation/v1/user/keys/query`
94///
95/// Gets devices and identity keys for the given users.
96pub(crate) async fn get_keys_route(
97	State(services): State<crate::State>,
98	body: Ruma<get_keys::v1::Request>,
99) -> Result<get_keys::v1::Response> {
100	if body
101		.device_keys
102		.iter()
103		.any(|(u, _)| !services.globals.user_is_local(u))
104	{
105		return Err!(Request(InvalidParam("User does not belong to this server.")));
106	}
107
108	let result = get_keys_helper(
109		&services,
110		None,
111		&body.device_keys,
112		|u| Some(u.server_name()) == body.origin.as_deref(),
113		services.config.allow_device_name_federation,
114	)
115	.await?;
116
117	Ok(get_keys::v1::Response {
118		device_keys: result.device_keys,
119		master_keys: result.master_keys,
120		self_signing_keys: result.self_signing_keys,
121	})
122}
123
124/// # `POST /_matrix/federation/v1/user/keys/claim`
125///
126/// Claims one-time keys.
127pub(crate) async fn claim_keys_route(
128	State(services): State<crate::State>,
129	body: Ruma<claim_keys::v1::Request>,
130) -> Result<claim_keys::v1::Response> {
131	if body
132		.one_time_keys
133		.iter()
134		.any(|(u, _)| !services.globals.user_is_local(u))
135	{
136		return Err!(Request(InvalidParam("Tried to access user from other server.")));
137	}
138
139	let result = claim_keys_helper(&services, &body.one_time_keys).await?;
140
141	Ok(claim_keys::v1::Response { one_time_keys: result.one_time_keys })
142}