Skip to main content

tuwunel_service/threepid/
mod.rs

1mod binding;
2mod canonical;
3mod pending;
4mod ratelimit;
5
6use std::{
7	collections::HashMap,
8	net::IpAddr,
9	sync::{Arc, Mutex},
10	time::Instant,
11};
12
13use ruma::{MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, thirdparty::Medium};
14use serde::{Deserialize, Serialize};
15use tuwunel_core::{Result, smallstr::SmallString, utils::MutexMap};
16use tuwunel_database::{Database, Map};
17
18pub use self::{canonical::canonicalize_email, pending::PendingOutcome};
19
20/// Token-bucket table keyed on a throttle axis: last-refill instant and
21/// remaining tokens per key.
22type Ratelimiter<K> = Mutex<HashMap<K, (Instant, f64)>>;
23
24/// Stack-string key for the per-address throttle bucket; the modal email
25/// canonical address fits inline.
26type EmailKey = SmallString<[u8; 48]>;
27
28/// Manages email threepid bindings, verification sessions, and request limits.
29///
30/// Persistent maps provide lookups from user to email and email to user.
31/// In-memory token buckets limit `requestToken` calls by caller IP and
32/// canonical address.
33pub struct Service {
34	db: Data,
35	pending_mutex: MutexMap<String, ()>,
36	claim_mutex: MutexMap<UiaaKey, ()>,
37	ip_ratelimiter: Ratelimiter<IpAddr>,
38	address_ratelimiter: Ratelimiter<EmailKey>,
39}
40
41struct Data {
42	database: Arc<Database>,
43	userid_email: Arc<Map>,
44	email_userid: Arc<Map>,
45	threepidsid_pending: Arc<Map>,
46	userdevicesessionid_threepid: Arc<Map>,
47}
48
49/// Stores a UIAA session identifier inline in the common case.
50///
51/// The 32-byte budget matches identifiers minted by the UIAA service.
52pub type UiaaSessionId = SmallString<[u8; 32]>;
53
54/// Identifies the exact UIAA session that owns a validated threepid.
55///
56/// Owned components let the durable claim key outlive an individual request.
57pub type UiaaKey = (OwnedUserId, OwnedDeviceId, UiaaSessionId);
58
59/// CBOR value of a `userid_email` row: the per-binding metadata, with the
60/// address carried in the composite key.
61#[derive(Clone, Debug, Deserialize, Serialize)]
62struct Binding {
63	medium: Medium,
64	validated_at: MilliSecondsSinceUnixEpoch,
65	added_at: MilliSecondsSinceUnixEpoch,
66}
67
68/// Validated `(medium, address)` pair handed back when a pending verification
69/// is consumed by the add flow.
70#[derive(Clone, Debug)]
71pub struct Association {
72	pub medium: Medium,
73	pub address: String,
74}
75
76impl crate::Service for Service {
77	fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
78		Ok(Arc::new(Self {
79			db: Data {
80				database: args.db.clone(),
81				userid_email: args.db["userid_email"].clone(),
82				email_userid: args.db["email_userid"].clone(),
83				threepidsid_pending: args.db["threepidsid_pending"].clone(),
84				userdevicesessionid_threepid: args.db["userdevicesessionid_threepid"].clone(),
85			},
86			pending_mutex: MutexMap::new(),
87			claim_mutex: MutexMap::new(),
88			ip_ratelimiter: Mutex::new(HashMap::new()),
89			address_ratelimiter: Mutex::new(HashMap::new()),
90		}))
91	}
92
93	fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
94}