diff --git a/src/ip_tracker/tests.rs b/src/ip_tracker/tests.rs index bf2925b..13e41ff 100644 --- a/src/ip_tracker/tests.rs +++ b/src/ip_tracker/tests.rs @@ -189,6 +189,39 @@ async fn test_clear_user_ips() { assert_eq!(tracker.get_active_ip_count("test_user").await, 0); } +#[tokio::test] +async fn stale_incarnation_cleanup_cannot_release_recreated_user_ip() { + let tracker = UserIpTracker::new(); + tracker.set_user_limit("test_user", 1).await; + let old_ip = test_ipv4(192, 168, 2, 1); + let current_ip = test_ipv4(192, 168, 2, 2); + let rejected_ip = test_ipv4(192, 168, 2, 3); + + tracker + .check_and_add_for_incarnation("test_user", 1, old_ip) + .await + .unwrap(); + tracker + .clear_user_ips_if_not_newer("test_user", 2) + .await; + tracker + .check_and_add_for_incarnation("test_user", 3, current_ip) + .await + .unwrap(); + + tracker + .remove_ip_for_incarnation("test_user", 1, old_ip) + .await; + + assert!(tracker.is_ip_active("test_user", current_ip).await); + assert!( + tracker + .check_and_add_for_incarnation("test_user", 3, rejected_ip) + .await + .is_err() + ); +} + #[tokio::test] async fn test_is_ip_active() { let tracker = UserIpTracker::new(); diff --git a/src/proxy/user_admission.rs b/src/proxy/user_admission.rs index 8efd9fb..4c9b835 100644 --- a/src/proxy/user_admission.rs +++ b/src/proxy/user_admission.rs @@ -538,61 +538,5 @@ fn cancel_owners( } #[cfg(test)] -mod tests { - use super::*; - - fn users(secret: &str) -> HashMap { - HashMap::from([("alice".to_string(), secret.to_string())]) - } - - #[test] - fn shared_authority_rejects_registration_through_an_old_generation() { - let authority = UserAdmissionAuthority::new(); - let secret = "00112233445566778899aabbccddeeff"; - authority.apply_config(&users(secret), &HashMap::new()); - let credential = credential_id_from_hex(secret).unwrap(); - - assert!(authority.claim_authenticated("alice", credential).is_some()); - authority.stage_user("alice", secret, false).unwrap(); - - assert!(authority.claim_authenticated("alice", credential).is_none()); - } - - #[test] - fn stale_credential_cannot_cross_delete_and_recreate() { - let authority = UserAdmissionAuthority::new(); - let old_secret = "00112233445566778899aabbccddeeff"; - let new_secret = "ffeeddccbbaa99887766554433221100"; - authority.apply_config(&users(old_secret), &HashMap::new()); - let old_credential = credential_id_from_hex(old_secret).unwrap(); - let old_incarnation = authority - .authenticated_incarnation("alice", old_credential) - .unwrap(); - - authority.delete_user("alice"); - let recreated = authority.stage_user("alice", new_secret, true).unwrap(); - - assert!(recreated.incarnation > old_incarnation); - assert!( - authority - .authenticated_incarnation("alice", old_credential) - .is_none() - ); - } - - #[test] - fn stale_candidate_cannot_overwrite_newer_mutation() { - let authority = UserAdmissionAuthority::new(); - let secret = "00112233445566778899aabbccddeeff"; - authority.apply_config(&users(secret), &HashMap::new()); - let candidate_epoch = authority.epoch(); - authority.stage_user("alice", secret, false).unwrap(); - - assert!( - authority - .apply_config_if_epoch(candidate_epoch, &users(secret), &HashMap::new()) - .is_none() - ); - assert!(!authority.is_user_enabled("alice")); - } -} +#[path = "user_admission/tests.rs"] +mod tests; diff --git a/src/proxy/user_admission/tests.rs b/src/proxy/user_admission/tests.rs new file mode 100644 index 0000000..8cc6592 --- /dev/null +++ b/src/proxy/user_admission/tests.rs @@ -0,0 +1,56 @@ +use super::*; + +fn users(secret: &str) -> HashMap { + HashMap::from([("alice".to_string(), secret.to_string())]) +} + +#[test] +fn shared_authority_rejects_registration_through_an_old_generation() { + let authority = UserAdmissionAuthority::new(); + let secret = "00112233445566778899aabbccddeeff"; + authority.apply_config(&users(secret), &HashMap::new()); + let credential = credential_id_from_hex(secret).unwrap(); + + assert!(authority.claim_authenticated("alice", credential).is_some()); + authority.stage_user("alice", secret, false).unwrap(); + + assert!(authority.claim_authenticated("alice", credential).is_none()); +} + +#[test] +fn stale_credential_cannot_cross_delete_and_recreate() { + let authority = UserAdmissionAuthority::new(); + let old_secret = "00112233445566778899aabbccddeeff"; + let new_secret = "ffeeddccbbaa99887766554433221100"; + authority.apply_config(&users(old_secret), &HashMap::new()); + let old_credential = credential_id_from_hex(old_secret).unwrap(); + let old_incarnation = authority + .authenticated_incarnation("alice", old_credential) + .unwrap(); + + authority.delete_user("alice"); + let recreated = authority.stage_user("alice", new_secret, true).unwrap(); + + assert!(recreated.incarnation > old_incarnation); + assert!( + authority + .authenticated_incarnation("alice", old_credential) + .is_none() + ); +} + +#[test] +fn stale_candidate_cannot_overwrite_newer_mutation() { + let authority = UserAdmissionAuthority::new(); + let secret = "00112233445566778899aabbccddeeff"; + authority.apply_config(&users(secret), &HashMap::new()); + let candidate_epoch = authority.epoch(); + authority.stage_user("alice", secret, false).unwrap(); + + assert!( + authority + .apply_config_if_epoch(candidate_epoch, &users(secret), &HashMap::new()) + .is_none() + ); + assert!(!authority.is_user_enabled("alice")); +}