Skip to main content

tuwunel_api/client/keys/
upload_signatures.rs

1use axum::extract::State;
2use futures::StreamExt;
3use ruma::{UserId, api::client::keys::upload_signatures};
4use serde_json::value::RawValue;
5use tuwunel_core::{Result, debug, debug_warn, utils::IterStream};
6
7use crate::Ruma;
8
9/// # `POST /_matrix/client/r0/keys/signatures/upload`
10///
11/// Uploads end-to-end key signatures from the sender user.
12pub(crate) async fn upload_signatures_route(
13	State(services): State<crate::State>,
14	body: Ruma<upload_signatures::v3::Request>,
15) -> Result<upload_signatures::v3::Response> {
16	let sender_user = body.sender_user();
17
18	if body.signed_keys.is_empty() {
19		debug!("Empty signed_keys sent in key signature upload");
20		return Ok(upload_signatures::v3::Response::new());
21	}
22
23	body.signed_keys
24		.iter()
25		.flat_map(|(user_id, keys)| {
26			keys.iter().flat_map(move |(key_id, key)| {
27				signatures_from_key(sender_user, key_id, key)
28					.map(move |sig| (user_id.as_ref(), key_id, sig))
29			})
30		})
31		.stream()
32		.for_each_concurrent(None, async |(user_id, key_id, signature)| {
33			services
34				.users
35				.sign_key(user_id, key_id, signature, sender_user)
36				.await
37				.inspect_err(|e| debug_warn!("{e}"))
38				.ok();
39		})
40		.await;
41
42	Ok(upload_signatures::v3::Response::default())
43}
44
45fn signatures_from_key(
46	sender_user: &UserId,
47	key_id: &str,
48	key: &RawValue,
49) -> impl Iterator<Item = (String, String)> + Send + 'static {
50	serde_json::to_value(key)
51		.inspect_err(|e| debug_warn!(?key_id, "Invalid \"key\" JSON: {e}"))
52		.ok()
53		.and_then(|key| key.get("signatures").cloned())
54		.and_then(|sigs| sigs.get(sender_user.to_string()).cloned())
55		.and_then(|val| val.as_object().cloned())
56		.into_iter()
57		.flatten()
58		.filter_map(|(sig_id, val)| Some((sig_id, val.as_str().map(ToOwned::to_owned)?)))
59}