Skip to main content

tuwunel_api/server/
user.rs

1use 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
21/// # `GET /_matrix/federation/v1/user/devices/{userId}`
22///
23/// Gets information on all devices of the user.
24pub(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
105/// # `POST /_matrix/federation/v1/user/keys/query`
106///
107/// Gets devices and identity keys for the given users.
108pub(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
136/// # `POST /_matrix/federation/v1/user/keys/claim`
137///
138/// Claims one-time keys.
139pub(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}