test: add comprehensive pairing code consumption tests

Add comprehensive tests for pairing code consumption feature
This commit is contained in:
Edvard Schøyen 2026-02-15 07:36:54 -05:00 committed by GitHub
parent dc654f6835
commit bd02d73ecc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -28,7 +28,7 @@ pub struct PairingGuard {
/// Whether pairing is required at all.
require_pairing: bool,
/// One-time pairing code (generated on startup, consumed on first pair).
pairing_code: Option<String>,
pairing_code: Mutex<Option<String>>,
/// Set of SHA-256 hashed bearer tokens (persisted across restarts).
paired_tokens: Mutex<HashSet<String>>,
/// Brute-force protection: failed attempt counter + lockout time.
@ -62,15 +62,18 @@ impl PairingGuard {
};
Self {
require_pairing,
pairing_code: code,
pairing_code: Mutex::new(code),
paired_tokens: Mutex::new(tokens),
failed_attempts: Mutex::new((0, None)),
}
}
/// The one-time pairing code (only set when no tokens exist yet).
pub fn pairing_code(&self) -> Option<&str> {
self.pairing_code.as_deref()
pub fn pairing_code(&self) -> Option<String> {
self.pairing_code
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone()
}
/// Whether pairing is required at all.
@ -97,23 +100,33 @@ impl PairingGuard {
}
}
if let Some(ref expected) = self.pairing_code {
if constant_time_eq(code.trim(), expected.trim()) {
// Reset failed attempts on success
{
let mut attempts = self
.failed_attempts
{
let mut pairing_code = self
.pairing_code
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(ref expected) = *pairing_code {
if constant_time_eq(code.trim(), expected.trim()) {
// Reset failed attempts on success
{
let mut attempts = self
.failed_attempts
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*attempts = (0, None);
}
let token = generate_token();
let mut tokens = self
.paired_tokens
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*attempts = (0, None);
tokens.insert(hash_token(&token));
// Consume the pairing code so it cannot be reused
*pairing_code = None;
return Ok(Some(token));
}
let token = generate_token();
let mut tokens = self
.paired_tokens
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
tokens.insert(hash_token(&token));
return Ok(Some(token));
}
}