tuwunel_service/admin/
register.rs1use std::{
15 collections::BTreeMap,
16 time::{Duration, Instant},
17};
18
19use tuwunel_core::{
20 implement,
21 utils::{self, Secret, is_secret_set, resolve_secret},
22};
23
24type Nonces = BTreeMap<String, Instant>;
25
26const NONCE_LENGTH: usize = 32;
27const NONCE_TTL: Duration = Duration::from_mins(1);
28const NONCE_CAP: usize = 2048;
29
30#[implement(super::Service)]
31pub fn issue_register_nonce(&self) -> String {
32 let nonce = utils::random_string(NONCE_LENGTH);
33 let mut nonces = self
34 .register_nonces
35 .lock()
36 .expect("nonce mutex not poisoned");
37
38 gc_expired(&mut nonces);
39 if nonces.len() >= NONCE_CAP {
40 drop_oldest(&mut nonces);
41 }
42
43 nonces.insert(nonce.clone(), Instant::now());
44 nonce
45}
46
47#[implement(super::Service)]
50pub fn consume_register_nonce(&self, nonce: &str) -> bool {
51 let mut nonces = self
52 .register_nonces
53 .lock()
54 .expect("nonce mutex not poisoned");
55
56 nonces
57 .remove(nonce)
58 .is_some_and(|issued| issued.elapsed() < NONCE_TTL)
59}
60
61#[implement(super::Service)]
64pub fn register_is_enabled(&self) -> bool {
65 let config = &self.services.server.config;
66
67 is_secret_set(
68 config.registration_shared_secret_file.as_deref(),
69 config.registration_shared_secret.as_deref(),
70 )
71}
72
73#[implement(super::Service)]
76pub fn register_shared_secret(&self) -> Option<Secret> {
77 let config = &self.services.server.config;
78
79 resolve_secret(
80 config.registration_shared_secret_file.as_deref(),
81 config.registration_shared_secret.as_deref(),
82 "registration shared secret",
83 )
84}
85
86fn drop_oldest(nonces: &mut Nonces) {
87 nonces
88 .iter()
89 .min_by_key(|(_, issued)| **issued)
90 .map(|(k, _)| k.clone())
91 .as_ref()
92 .map(|oldest| nonces.remove(oldest));
93}
94
95fn gc_expired(nonces: &mut Nonces) { nonces.retain(|_, issued| issued.elapsed() < NONCE_TTL); }