diff --git a/crates/rules/src/matrix_builder.rs b/crates/rules/src/matrix_builder.rs index 51227ee2..f445bb06 100644 --- a/crates/rules/src/matrix_builder.rs +++ b/crates/rules/src/matrix_builder.rs @@ -591,6 +591,230 @@ mod tests { } } + mod validation_for_addition_of_factor_source_for_each { + use super::*; + + mod primary { + + use super::*; + + #[test] + fn empty() { + let sut = make(); + let xs = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &IndexSet::new(), + ); + assert_eq!(xs, IndexSet::new()); + } + + #[test] + fn device_threshold_3x_first_ok_second_not() { + let mut sut = make(); + let xs = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ) + ] + ); + + sut.add_factor_source_to_primary_threshold(FactorSourceID::sample_device()) + .unwrap(); + + let xs = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + ), + ] + ); + } + + #[test] + fn device_2x_threshold_override_first_ok_second_not() { + let mut sut = make(); + let xs = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ) + ] + ); + + sut.add_factor_source_to_primary_threshold(FactorSourceID::sample_device()) + .unwrap(); + + let xs = sut.validation_for_addition_of_factor_source_to_primary_override_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + ), + ] + ); + } + + #[test] + fn device_threshold_override_2x_first_ok_second_not() { + let mut sut = make(); + let xs = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ) + ] + ); + + sut.add_factor_source_to_primary_override(FactorSourceID::sample_device()) + .unwrap(); + + let xs = sut.validation_for_addition_of_factor_source_to_primary_override_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + ), + ] + ); + } + + #[test] + fn device_2x_override_threshold_first_ok_second_not() { + let mut sut = make(); + let xs = sut.validation_for_addition_of_factor_source_to_primary_override_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ) + ] + ); + + sut.add_factor_source_to_primary_override(FactorSourceID::sample_device()) + .unwrap(); + + let xs = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ]), + ); + + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + sargon::RoleKind::Primary, + FactorSourceID::sample_device_other(), + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + ), + ] + ); + } + } + } + mod validation_of_addition_of_kind { use super::*; diff --git a/crates/rules/src/roles_builder.rs b/crates/rules/src/roles_builder.rs index fc171aa9..31b18881 100644 --- a/crates/rules/src/roles_builder.rs +++ b/crates/rules/src/roles_builder.rs @@ -213,6 +213,51 @@ pub struct FactorSourceInRoleBuilderValidationStatus { pub validation: RoleBuilderMutateResult, } +impl FactorSourceInRoleBuilderValidationStatus { + pub(super) fn new( + role: RoleKind, + factor_source_id: FactorSourceID, + validation: RoleBuilderMutateResult, + ) -> Self { + Self { + role, + factor_source_id, + validation, + } + } +} + +#[cfg(test)] +impl FactorSourceInRoleBuilderValidationStatus { + pub(super) fn ok(role: RoleKind, factor_source_id: FactorSourceID) -> Self { + Self::new(role, factor_source_id, Ok(())) + } + + pub(super) fn forever_invalid( + role: RoleKind, + factor_source_id: FactorSourceID, + reason: ForeverInvalidReason, + ) -> Self { + Self::new( + role, + factor_source_id, + RoleBuilderMutateResult::forever_invalid(reason), + ) + } + + pub(super) fn not_yet_valid( + role: RoleKind, + factor_source_id: FactorSourceID, + reason: NotYetValidReason, + ) -> Self { + Self::new( + role, + factor_source_id, + RoleBuilderMutateResult::not_yet_valid(reason), + ) + } +} + pub type RoleBuilderMutateResult = Result<(), RoleBuilderValidation>; pub type RoleBuilderBuildResult = Result; @@ -398,11 +443,11 @@ pub trait BuilderOfFactorsInRole: Sized + private::UncheckedBuilderOfFactorsInRo factor_source_id, factor_list_kind, ); - FactorSourceInRoleBuilderValidationStatus { - role: self.role(), - factor_source_id: *factor_source_id, - validation: validation_status, - } + FactorSourceInRoleBuilderValidationStatus::new( + self.role(), + *factor_source_id, + validation_status, + ) }) .collect() } @@ -1070,16 +1115,11 @@ mod tests { assert_eq!( xs.into_iter().collect::>(), vec![ - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Recovery, - factor_source_id: sample(), - validation: Ok(()) - }, - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Recovery, - factor_source_id: sample_other(), - validation: Ok(()) - } + FactorSourceInRoleBuilderValidationStatus::ok(RoleKind::Recovery, sample()), + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Recovery, + sample_other(), + ) ] ); } @@ -2111,23 +2151,19 @@ mod tests { assert_eq!( xs.into_iter().collect::>(), vec![ - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Primary, - factor_source_id: fs0, - validation: Ok(()) - }, - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Primary, - factor_source_id: fs1, - validation: MutRes::not_yet_valid( + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Primary, + fs0, + ), + FactorSourceInRoleBuilderValidationStatus::not_yet_valid( + RoleKind::Primary, + fs1, NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor - ) - }, - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Primary, - factor_source_id: fs2, - validation: Ok(()) - }, + ), + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Primary, + fs2, + ), ] ); _ = sut.add_factor_source_to_list(fs0, list()); @@ -2140,23 +2176,13 @@ mod tests { assert_eq!( xs.into_iter().collect::>(), vec![ - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Primary, - factor_source_id: fs0, - validation: MutRes::forever_invalid( - ForeverInvalidReason::FactorSourceAlreadyPresent - ) - }, - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Primary, - factor_source_id: fs1, - validation: Ok(()) - }, - FactorSourceInRoleBuilderValidationStatus { - role: RoleKind::Primary, - factor_source_id: fs2, - validation: Ok(()) - }, + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Primary, + fs0, + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::ok(RoleKind::Primary, fs1,), + FactorSourceInRoleBuilderValidationStatus::ok(RoleKind::Primary, fs2,), ] ); }