From 985ece11525c8ad5c122f0c5ed29b76045f12e81 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 30 Nov 2024 13:25:56 +0100 Subject: [PATCH 01/70] [no ci] WIP Rules for Roles --- .../mfa/security_structures/builder.rs | 537 +++++ .../decl_security_structure_of.rs | 155 -- .../security_structures/error_conversion.rs | 28 + .../profile/mfa/security_structures/mod.rs | 5 +- ...ource_in_role_builder_validation_status.rs | 18 + .../mfa/security_structures/models/mod.rs | 3 + .../security_structure_of_factor_instances.rs | 5 - ...security_structure_of_factor_source_ids.rs | 7 - .../security_structure_of_factor_sources.rs | 9 - .../factor_instances_provider_unit_tests.rs | 655 +++--- ...curify_entity_factor_instances_provider.rs | 295 +-- crates/sargon/src/lib.rs | 4 +- .../account/query_security_structures.rs | 18 + .../decl_security_structure_of.rs | 378 ---- ...confirmation_role_with_factor_instances.rs | 48 - .../matrix_of_factor_instances.rs | 202 -- .../factor_instance_level/mod.rs | 11 - .../primary_role_with_factor_instances.rs | 63 - .../recovery_role_with_factor_instances.rs | 49 - .../security_structure_of_factor_instances.rs | 75 - ...onfirmation_role_with_factor_source_ids.rs | 52 - .../matrix_of_factor_source_ids.rs | 50 - .../factor_source_id_level/mod.rs | 15 - .../primary_role_with_factor_source_ids.rs | 49 - .../recovery_role_with_factor_source_ids.rs | 46 - ...security_structure_of_factor_source_ids.rs | 56 - ...ecurity_structures_of_factor_source_ids.rs | 19 - .../matrix_of_factor_sources.rs | 39 - .../factor_source_level/mod.rs | 12 - .../primary_role_with_factor_sources.rs | 47 - .../security_structure_of_factor_sources.rs | 213 -- .../security_structures_of_factor_sources.rs | 19 - .../abstract_matrix_builder_or_built.rs | 50 + .../matrices/builder/error.rs | 64 + .../matrices/builder/matrix_builder.rs | 376 ++++ .../builder/matrix_builder_unit_tests.rs | 1976 +++++++++++++++++ .../matrices/builder/matrix_template.rs | 488 ++++ .../matrices/builder/mod.rs | 9 + .../matrices/factor_source_id_samples.rs | 72 + .../matrices/matrix_of_factor_instances.rs | 409 ++++ .../matrices/matrix_of_factor_source_ids.rs | 315 +++ .../matrices/matrix_of_factor_sources.rs | 70 + .../mfa/security_structures/matrices/mod.rs | 14 + .../profile/mfa/security_structures/mod.rs | 18 +- .../security_structures/role_with_factors.rs | 18 - .../roles/abstract_role_builder_or_built.rs | 147 ++ .../confirmation_roles_builder_unit_tests.rs | 350 +++ .../security_structures/roles/builder/mod.rs | 7 + .../primary_roles_builder_unit_tests.rs | 952 ++++++++ .../recovery_roles_builder_unit_tests.rs | 426 ++++ .../roles/builder/roles_builder.rs | 857 +++++++ .../roles/builder/roles_builder_unit_tests.rs | 88 + ...confirmation_role_with_factor_instances.rs | 33 + ...archical_deterministic_factor_instances.rs | 295 +++ .../factor_instance_level/mod.rs | 11 + .../primary_role_with_factor_instances.rs | 111 + .../recovery_role_with_factor_instances.rs | 33 + .../role_with_factor_instances.rs | 79 + ...onfirmation_role_with_factor_source_ids.rs | 102 + .../factor_source_id_level/mod.rs | 9 + .../primary_role_with_factor_source_ids.rs | 106 + .../recovery_role_with_factor_source_ids.rs | 65 + .../roles_with_factor_ids.rs | 4 + .../factor_source_kind_level/mod.rs | 3 + .../factor_source_kind_level/role_template.rs | 105 + .../confirmation_role_with_factor_sources.rs | 27 +- .../factor_levels/factor_source_level/mod.rs | 6 + .../primary_role_with_factor_sources.rs | 37 + .../recovery_role_with_factor_sources.rs | 25 +- .../roles_with_factor_sources.rs | 36 + .../roles/factor_levels/mod.rs | 9 + .../mfa/security_structures/roles/mod.rs | 7 + .../security_structure_id.rs | 0 .../abstract_security_structure_of_factors.rs | 47 + .../security_structure_of_factors/mod.rs | 7 + ...security_structure_of_factor_source_ids.rs | 218 ++ .../security_structure_of_factor_sources.rs | 92 + .../profile/v100/app_preferences/security.rs | 21 + .../signing/collector/signatures_collector.rs | 17 +- .../general_role_with_hd_factor_instance.rs | 225 -- .../sargon/src/signing/petition_types/mod.rs | 2 - .../petition_types/petition_for_entity.rs | 6 +- .../src/types/samples/account_samples.rs | 4 + .../src/types/samples/persona_samples.rs | 3 + 84 files changed, 9250 insertions(+), 2383 deletions(-) create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs delete mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/decl_security_structure_of.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/decl_security_structure_of.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_instance_level/confirmation_role_with_factor_instances.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_instance_level/matrix_of_factor_instances.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_instance_level/mod.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_instance_level/primary_role_with_factor_instances.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_instance_level/recovery_role_with_factor_instances.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_instance_level/security_structure_of_factor_instances.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/confirmation_role_with_factor_source_ids.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/matrix_of_factor_source_ids.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/mod.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/primary_role_with_factor_source_ids.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/recovery_role_with_factor_source_ids.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structure_of_factor_source_ids.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structures_of_factor_source_ids.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_level/matrix_of_factor_sources.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_level/mod.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_level/primary_role_with_factor_sources.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structure_of_factor_sources.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structures_of_factor_sources.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/role_with_factors.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder_unit_tests.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/general_role_with_hierarchical_deterministic_factor_instances.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs rename crates/sargon/src/profile/mfa/security_structures/{ => roles/factor_levels}/factor_source_level/confirmation_role_with_factor_sources.rs (53%) create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs rename crates/sargon/src/profile/mfa/security_structures/{ => roles/factor_levels}/factor_source_level/recovery_role_with_factor_sources.rs (54%) create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/mod.rs rename crates/sargon/src/profile/mfa/security_structures/{factor_source_id_level => }/security_structure_id.rs (100%) create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/mod.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs delete mode 100644 crates/sargon/src/signing/petition_types/general_role_with_hd_factor_instance.rs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs new file mode 100644 index 000000000..5e24d63f8 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -0,0 +1,537 @@ +#![allow(clippy::new_without_default)] +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::sync::{Arc, RwLock}; + +use sargon::IndexSet; + +use crate::prelude::*; + +#[derive(Debug, uniffi::Object)] +pub struct SecurityShieldBuilder { + wrapped: RwLock>, + name: RwLock, +} + +#[derive(Debug, PartialEq, Eq, Hash, uniffi::Object)] +#[uniffi::export(Debug, Eq, Hash)] +pub struct SecurityStructureOfFactorSourceIds { + pub wrapped: rules::SecurityStructureOfFactorSourceIds, +} + +impl SecurityShieldBuilder { + fn get( + &self, + with_non_consumed_builder: impl Fn(&MatrixBuilder) -> R, + ) -> R { + let binding = self.wrapped.read().unwrap(); + + let Some(builder) = binding.as_ref() else { + unreachable!("Already built, should not have happened.") + }; + with_non_consumed_builder(builder) + } + + fn with>( + &self, + mut with_non_consumed_builder: impl FnMut( + &mut MatrixBuilder, + ) -> Result, + ) -> Result { + let guard = self.wrapped.write(); + + let mut binding = + guard.map_err(|_| CommonError::MatrixBuilderRwLockPoisoned)?; + + let Some(builder) = binding.as_mut() else { + return Err(CommonError::AlreadyBuilt); + }; + with_non_consumed_builder(builder) + .map_err(|e| Into::::into(e)) + } + + fn validation_for_addition_of_factor_source_by_calling( + &self, + factor_sources: Vec>, + call: impl Fn( + &MatrixBuilder, + &IndexSet, + ) + -> IndexSet, + ) -> Result>, CommonError> { + let input = &factor_sources + .clone() + .into_iter() + .map(|x| x.inner) + .collect::>(); + self.with(|builder| { + let xs = call(builder, input); + + let xs = xs + .into_iter() + .map(Into::::into) + .map(Arc::new) + .collect(); + + Ok::<_, CommonError>(xs) + }) + } +} + +#[uniffi::export] +impl SecurityShieldBuilder { + #[uniffi::constructor] + pub fn new() -> Arc { + Arc::new(Self { + wrapped: RwLock::new(Some(MatrixBuilder::new())), + name: RwLock::new("My Shield".to_owned()), + }) + } +} + +impl SecurityShieldBuilder { + fn get_factors( + &self, + access: impl Fn(&MatrixBuilder) -> &Vec, + ) -> Vec> { + self.get(|builder| { + let factors = access(builder); + factors.iter().map(FactorSourceID::new).collect::>() + }) + } +} + +// ==================== +// ==== GET / READ ==== +// ==================== +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn get_primary_threshold(&self) -> u8 { + self.get(|builder| builder.get_threshold()) + } + + pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { + self.get(|builder| builder.get_number_of_days_until_auto_confirm()) + } + + pub fn get_name(&self) -> String { + self.name.read().unwrap().clone() + } + + pub fn get_primary_threshold_factors(&self) -> Vec> { + self.get_factors(|builder| builder.get_primary_threshold_factors()) + } + + pub fn get_primary_override_factors(&self) -> Vec> { + self.get_factors(|builder| builder.get_primary_override_factors()) + } + + pub fn get_recovery_factors(&self) -> Vec> { + self.get_factors(|builder| builder.get_recovery_factors()) + } + + pub fn get_confirmation_factors(&self) -> Vec> { + self.get_factors(|builder| builder.get_confirmation_factors()) + } +} + +// ==================== +// ===== MUTATION ===== +// ==================== +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn set_name(&self, name: String) { + *self.name.write().unwrap() = name + } + + /// Adds the factor source to the primary role threshold list. + pub fn add_factor_source_to_primary_threshold( + &self, + factor_source_id: Arc, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder + .add_factor_source_to_primary_threshold(factor_source_id.inner) + }) + } + + pub fn add_factor_source_to_primary_override( + &self, + factor_source_id: Arc, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder + .add_factor_source_to_primary_override(factor_source_id.inner) + }) + } + + pub fn remove_factor( + &self, + factor_source_id: Arc, + ) -> Result<(), CommonError> { + self.with(|builder| builder.remove_factor(&factor_source_id.inner)) + } + + pub fn set_threshold(&self, threshold: u8) -> Result<(), CommonError> { + self.with(|builder| builder.set_threshold(threshold)) + } + + pub fn set_number_of_days_until_auto_confirm( + &self, + number_of_days: u16, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.set_number_of_days_until_auto_confirm(number_of_days) + }) + } + + pub fn add_factor_source_to_recovery_override( + &self, + factor_source_id: Arc, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder + .add_factor_source_to_recovery_override(factor_source_id.inner) + }) + } + + pub fn add_factor_source_to_confirmation_override( + &self, + factor_source_id: Arc, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.add_factor_source_to_confirmation_override( + factor_source_id.inner, + ) + }) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + factor_source_kind.into(), + ) + }) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_recovery_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_recovery_override( + factor_source_kind.into(), + ) + }) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_primary_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_primary_override( + factor_source_kind.into(), + ) + }) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + &self, + factor_source_kind: FactorSourceKind, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + factor_source_kind.into(), + ) + }) + } + + pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &self, + factor_sources: Vec>, + ) -> Result>, CommonError> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder + .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( + &self, + factor_sources: Vec>, + ) -> Result>, CommonError> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder.validation_for_addition_of_factor_source_to_primary_override_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( + &self, + factor_sources: Vec>, + ) -> Result>, CommonError> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder + .validation_for_addition_of_factor_source_to_recovery_override_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( + &self, + factor_sources: Vec>, + ) -> Result>, CommonError> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder.validation_for_addition_of_factor_source_to_confirmation_override_for_each( + input, + ) + }, + ) + } + + pub fn build( + self: Arc, + ) -> Result { + let mut binding = self + .wrapped + .write() + .map_err(|_| CommonError::MatrixBuilderRwLockPoisoned)?; + let builder = binding.take().ok_or(CommonError::AlreadyBuilt)?; + let wrapped_matrix = builder + .build() + .map_err(|e| CommonError::BuildError(format!("{:?}", e)))?; + + let name = self.get_name(); + let display_name = sargon::DisplayName::new(name) + .map_err(|e| CommonError::Sargon(format!("{:?}", e)))?; + let wrapped_shield = rules::SecurityStructureOfFactorSourceIds::new( + display_name, + wrapped_matrix, + ); + + let shield = SecurityStructureOfFactorSourceIds { + wrapped: wrapped_shield, + }; + Ok(shield) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = SecurityShieldBuilder; + + #[test] + fn test() { + let sut = SUT::new(); + + assert_eq!(sut.get_name(), "My Shield"); + sut.set_name("S.H.I.E.L.D.".to_owned()); + + assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); + sut.set_number_of_days_until_auto_confirm(u16::MAX).unwrap(); + assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); + + // Primary + let sim_prim = + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]); + + let sim_prim_threshold = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus(), + ]); + + let sim_kind_prim = sut + .validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + + let sim_kind_prim_threshold = sut + .validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + assert_eq!( + sut.get_primary_threshold_factors(), + vec![FactorSourceID::sample_device()] + ); + _ = sut.set_threshold(1); + assert_eq!(sut.get_primary_threshold(), 1); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_arculus_other(), + ) + .unwrap(); + + assert_eq!( + sut.get_primary_override_factors(), + vec![ + FactorSourceID::sample_arculus(), + FactorSourceID::sample_arculus_other() + ] + ); + + // Recovery + let sim_rec = + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]); + + let sim_kind_rec = sut + .validation_for_addition_of_factor_source_of_kind_to_recovery_override( + FactorSourceKind::ArculusCard, + ); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + + assert_eq!( + sut.get_recovery_factors(), + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other() + ] + ); + + // Confirmation + let sim_conf = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]); + + let sim_kind_conf = sut + .validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + FactorSourceKind::ArculusCard, + ); + + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + + assert_eq!( + sut.get_confirmation_factors(), + vec![FactorSourceID::sample_device(),] + ); + + assert_ne!( + sim_prim, + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]) + ); + + assert_ne!( + sim_prim_threshold, + sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus() + ]) + ); + + assert_ne!( + sim_rec, + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]) + ); + + assert_ne!( + sim_conf, + sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]) + ); + + assert_ne!( + sim_kind_prim, + sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ) + ); + + assert_ne!( + sim_kind_prim_threshold, + sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ) + ); + + assert_eq!( + sim_kind_rec, + sut.validation_for_addition_of_factor_source_of_kind_to_recovery_override( + FactorSourceKind::ArculusCard, + ) + ); + + assert_eq!( + sim_kind_conf, + sut.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + FactorSourceKind::ArculusCard, + ) + ); + + sut.remove_factor(FactorSourceID::sample_arculus_other()) + .unwrap(); + sut.remove_factor(FactorSourceID::sample_ledger_other()) + .unwrap(); + + let shield = sut.build().unwrap(); + assert_eq!(shield.wrapped.metadata.display_name.value, "S.H.I.E.L.D."); + assert_eq!( + shield + .wrapped + .matrix_of_factors + .primary() + .get_override_factors(), + &vec![FactorSourceID::sample_arculus().inner] + ); + assert_eq!( + shield + .wrapped + .matrix_of_factors + .recovery() + .get_override_factors(), + &vec![FactorSourceID::sample_ledger().inner] + ); + assert_eq!( + shield + .wrapped + .matrix_of_factors + .confirmation() + .get_override_factors(), + &vec![FactorSourceID::sample_device().inner] + ); + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/decl_security_structure_of.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/decl_security_structure_of.rs deleted file mode 100644 index ac6a582eb..000000000 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/decl_security_structure_of.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::prelude::*; - -use sargon::HiddenConstructor as InternalHiddenConstructor; - -#[derive( - Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, uniffi::Record, -)] -pub struct HiddenConstructor; - -impl From for HiddenConstructor { - fn from(_value: InternalHiddenConstructor) -> Self { - HiddenConstructor - } -} -#[allow(clippy::from_over_into)] -impl Into for HiddenConstructor { - #[allow(clippy::from_over_into)] - fn into(self) -> InternalHiddenConstructor { - InternalHiddenConstructor - } -} - -macro_rules! decl_role_with_factors { - ( - $( - #[doc = $expr: expr] - )* - $role: ident, - $factor: ident - ) => { - paste! { - use sargon::[< $role RoleWith $factor s >] as [< Internal $role RoleWith $factor s >]; - - $( - #[doc = $expr] - )* - #[derive( - Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record, - )] - pub struct [< $role RoleWith $factor s >] { - /// Factors which are used in combination with other instances, amounting to at - /// least `threshold` many instances to perform some function with this role. - /// - /// # Implementation - /// Must allow duplicates, thus using `Vec` since at FactorSourceKind level - /// we might wanna use duplicates, allowing us to build a "template" - /// structure where a role might contain two `FactorSourceKind::TrustedContact`, - /// meaning an instance of this template at FactorSource level - /// (`SecurityStructureOfFactorSources`) will contain two different - /// `TrustedContactFactorSource`s. - pub threshold_factors: Vec<$factor>, - - /// How many threshold factors that must be used to perform some function with this role. - pub threshold: u8, - - /// Overriding / Super admin / "sudo" / God / factors, **ANY** - /// single of these factor which can perform the function of this role, - /// disregarding of `threshold`. - pub override_factors: Vec<$factor>, - } - } - }; -} - -pub(crate) use decl_role_with_factors; - -macro_rules! decl_matrix_of_factors { - ( - $( - #[doc = $expr: expr] - )* - $factor: ident - ) => { - paste! { - use sargon::[< MatrixOf $factor s >] as [< InternalMatrixOf $factor s >]; - - decl_role_with_factors!( - /// PrimaryRole is used for Signing Transactions. - Primary, - $factor - ); - - decl_role_with_factors!( - /// RecoveryRole is used to recover lost access to an entity. - Recovery, - $factor - ); - - decl_role_with_factors!( - /// ConfirmationRole is used to confirm recovery. - Confirmation, - $factor - ); - - $( - #[doc = $expr] - )* - #[derive( - Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record, - )] - pub struct [< MatrixOf $factor s >] { - /// Used for Signing transactions - pub primary_role: [< PrimaryRoleWith $factor s >], - - /// Used to initiate recovery - resetting the used Security Shield - /// of an entity. - pub recovery_role: [< RecoveryRoleWith $factor s >], - - /// To confirm recovery. - pub confirmation_role: [< ConfirmationRoleWith $factor s >], - } - } - }; -} - -pub(crate) use decl_matrix_of_factors; - -macro_rules! decl_security_structure_of { - ( - $( - #[doc = $expr: expr] - )* - $factor: ident, - ) => { - - decl_matrix_of_factors!($factor); - - paste! { - use sargon::[< SecurityStructureOf $factor s >] as [< InternalSecurityStructureOf $factor s >]; - - $( - #[doc = $expr] - )* - #[derive( - Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record, - )] - pub struct [< SecurityStructureOf $factor s >] { - /// Metadata of this Security Structure, such as globally unique and - /// stable identifier, creation date and user chosen label (name). - pub metadata: SecurityStructureMetadata, - - /// The amount of time until Confirmation Role is automatically - /// exercised, inputted by user in Days in UI, but translate it into - /// epochs ("block time"). - pub number_of_epochs_until_auto_confirmation: u64, - - /// The structure of factors to use for certain roles, Primary, Recovery - /// and Confirmation role. - pub matrix_of_factors: [< MatrixOf $factor s >], - } - } - }; -} - -pub(crate) use decl_security_structure_of; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs new file mode 100644 index 000000000..7022a6218 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs @@ -0,0 +1,28 @@ +use crate::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, uniffi::Error)] +pub enum CommonError { + #[error("Sargon")] + Sargon(String), + + #[error("AlreadyBuilt")] + AlreadyBuilt, + + #[error("Matrix builder RwLock poisoned")] + MatrixBuilderRwLockPoisoned, + + #[error("Build error {0}")] + BuildError(String), +} + +impl From for CommonError { + fn from(val: MatrixBuilderValidation) -> Self { + CommonError::BuildError(format!("{:?}", val)) + } +} + +impl From for CommonError { + fn from(val: RoleBuilderValidation) -> Self { + CommonError::BuildError(format!("{:?}", val)) + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index 4a9f0c16d..945274071 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,11 +1,12 @@ -mod decl_security_structure_of; +mod builder; +mod error_conversion; +mod models; mod security_structure_id; mod security_structure_metadata; mod security_structure_of_factor_instances; mod security_structure_of_factor_source_ids; mod security_structure_of_factor_sources; -pub use decl_security_structure_of::*; pub use security_structure_id::*; pub use security_structure_metadata::*; pub use security_structure_of_factor_instances::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs new file mode 100644 index 000000000..1f6b352ca --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -0,0 +1,18 @@ +#[derive(Debug, Clone, PartialEq, Eq, Hash, uniffi::Object)] +pub struct FactorSourceValidationStatus { + pub role: sargon::RoleKind, + pub factor_source_id: sargon::FactorSourceID, + pub validation: rules::RoleBuilderMutateResult, +} + +impl From + for FactorSourceValidationStatus +{ + fn from(val: rules::FactorSourceInRoleBuilderValidationStatus) -> Self { + FactorSourceValidationStatus { + role: val.role, + factor_source_id: val.factor_source_id, + validation: val.validation, + } + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs new file mode 100644 index 000000000..81c8aa5aa --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs @@ -0,0 +1,3 @@ +mod factor_source_in_role_builder_validation_status; + +pub use factor_source_in_role_builder_validation_status::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs index c6ea1a926..6983fa330 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs @@ -1,11 +1,6 @@ use crate::prelude::*; use sargon::SecurityStructureOfFactorInstances as InternalSecurityStructureOfFactorInstances; -decl_matrix_of_factors!( - /// A matrix of FactorInstances - FactorInstance -); - #[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] pub struct SecurityStructureOfFactorInstances { /// The ID of the `SecurityStructureOfFactorSourceIDs` in diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs index 045453a7e..871a05527 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs @@ -1,12 +1,5 @@ use crate::prelude::*; -decl_security_structure_of!( - /// A security structure at FactorSourceID level, this is - /// what is serialized and store into Profile, we convert - /// into this structure from `SecurityStructureOfFactorSources`. - FactorSourceID, -); - decl_vec_samples_for!( SecurityStructuresOfFactorSourceIDs, SecurityStructureOfFactorSourceIDs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs index fce80ecd6..42c3d70e1 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs @@ -1,14 +1,5 @@ use crate::prelude::*; -decl_security_structure_of!( - /// Security structure at `FactorSource` level. - /// This is what user view, creates and manages. - /// - /// Before it gets saved into Profile gets converted into - /// `SecurityStructureOfFactorSourceIDs` - FactorSource, -); - #[uniffi::export] pub fn new_security_structure_of_factor_sources_sample( ) -> SecurityStructureOfFactorSources { diff --git a/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs b/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs index cbcc03627..a4796160d 100644 --- a/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs +++ b/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs @@ -462,27 +462,28 @@ async fn cache_is_unchanged_in_case_of_failure() { assert_eq!(all_accounts.len(), 3 * n); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + let shield_0 = SecurityStructureOfFactorSources::sample(); let all_accounts = os .profile() @@ -610,27 +611,28 @@ async fn test_assert_factor_instances_invalid() { .unwrap(); let bdfs = FactorSource::from(os.bdfs().unwrap()); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + let shield_0 = SecurityStructureOfFactorSources::sample(); let (security_structure_of_fis, _, _) = os.make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome(IndexSet::from_iter([alice.address()]), shield_0.clone()).await.unwrap(); @@ -949,30 +951,31 @@ async fn test_securified_accounts() { os.add_factor_source(arculus.clone()).await.unwrap(); os.add_factor_source(password.clone()).await.unwrap(); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + let shield_0 = SecurityStructureOfFactorSources::sample(); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -993,7 +996,7 @@ async fn test_securified_accounts() { let alice_sec = security_structures_of_fis.get(&alice.address()).unwrap(); let alice_matrix = alice_sec.matrix_of_factors.clone(); - assert_eq!(alice_matrix.primary_role.threshold, 2); + assert_eq!(alice_matrix.primary().get_threshold(), 2); assert_eq!( alice_matrix @@ -1032,7 +1035,7 @@ async fn test_securified_accounts() { let bob_sec = security_structures_of_fis.get(&bob.address()).unwrap(); let bob_matrix = bob_sec.matrix_of_factors.clone(); - assert_eq!(bob_matrix.primary_role.threshold, 2); + assert_eq!(bob_matrix.primary().get_threshold(), 2); assert_eq!( bob_matrix @@ -1086,21 +1089,22 @@ async fn test_securified_accounts() { "First account created with ledger, should have index 0, even though this ledger was used in the shield, since we are using two different KeySpaces for Securified and Unsecurified accounts." ); - let matrix_1 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([password.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([password.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([password.clone()]) - .unwrap(), - ) - .unwrap(); - - let shield_1 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), - 14, - matrix_1, - ); + // let matrix_1 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([password.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([password.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([password.clone()]) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_1 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), + // 14, + // matrix_1, + // ); + let shield_1 = SecurityStructureOfFactorSources::sample_other(); let (security_structures_of_fis, instances_in_cache_consumer, _) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1151,7 +1155,7 @@ async fn test_securified_accounts() { .await .unwrap(); - // dont forget to consume! + // Don't forget to consume! instances_in_cache_consumer.consume().await.unwrap(); assert!( @@ -1212,27 +1216,28 @@ async fn securify_accounts_when_cache_is_half_full_single_factor_source() { .await .unwrap(); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + let shield_0 = SecurityStructureOfFactorSources::sample(); let profile = os.profile().unwrap(); let all_accounts = profile @@ -1357,30 +1362,32 @@ async fn securify_accounts_when_cache_is_half_full_multiple_factor_sources() { assert_eq!(derivation_outcome.debug_was_derived.len(), 3 * n); // `n` missing + CACHE filling 2*n more. - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + + let shield_0 = SecurityStructureOfFactorSources::sample(); let all_accounts = os .profile() @@ -1595,27 +1602,29 @@ async fn securify_personas_when_cache_is_half_full_single_factor_source() { let (_, _) = os.batch_create_many_personas_with_bdfs_with_derivation_outcome_then_save_once(3 * n as u16, NetworkID::Mainnet, "Persona".to_owned()).await.unwrap(); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone()], - 1, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone()], + // 1, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + + let shield_0 = SecurityStructureOfFactorSources::sample(); let all_personas = os .profile() @@ -1726,19 +1735,21 @@ async fn create_single_account() { "should have used cache" ); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([bdfs.clone()]).unwrap(), - RecoveryRoleWithFactorSources::override_only([bdfs.clone()]).unwrap(), - ConfirmationRoleWithFactorSources::override_only([bdfs.clone()]) - .unwrap(), - ) - .unwrap(); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([bdfs.clone()]).unwrap(), + // RecoveryRoleWithFactorSources::override_only([bdfs.clone()]).unwrap(), + // ConfirmationRoleWithFactorSources::override_only([bdfs.clone()]) + // .unwrap(), + // ) + // .unwrap(); - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ) + + let shield_0 = SecurityStructureOfFactorSources::sample(); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1807,30 +1818,32 @@ async fn securified_personas() { os.add_factor_source(arculus.clone()).await.unwrap(); os.add_factor_source(password.clone()).await.unwrap(); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [bdfs.clone(), ledger.clone(), arculus.clone()], - 2, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [bdfs.clone(), ledger.clone(), arculus.clone()], + // 2, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + + let shield_0 = SecurityStructureOfFactorSources::sample(); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1851,7 +1864,7 @@ async fn securified_personas() { let batman_sec = security_structures_of_fis.get(&batman.address()).unwrap(); let batman_matrix = batman_sec.matrix_of_factors.clone(); - assert_eq!(batman_matrix.primary_role.threshold, 2); + assert_eq!(batman_matrix.primary().get_threshold(), 2); assert_eq!( batman_matrix @@ -1891,7 +1904,7 @@ async fn securified_personas() { security_structures_of_fis.get(&satoshi.address()).unwrap(); let satoshi_matrix = satoshi_sec.matrix_of_factors.clone(); - assert_eq!(satoshi_matrix.primary_role.threshold, 2); + assert_eq!(satoshi_matrix.primary().get_threshold(), 2); assert_eq!( satoshi_matrix @@ -1945,21 +1958,23 @@ async fn securified_personas() { "First persona created with ledger, should have index 0, even though this ledger was used in the shield, since we are using two different KeySpaces for Securified and Unsecurified personas." ); - let matrix_1 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([password.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([password.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([password.clone()]) - .unwrap(), - ) - .unwrap(); + // let matrix_1 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([password.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([password.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([password.clone()]) + // .unwrap(), + // ) + // .unwrap(); - let shield_1 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), - 14, - matrix_1, - ); + // let shield_1 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), + // 14, + // matrix_1, + // ); + + let shield_1 = SecurityStructureOfFactorSources::sample_other(); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -2099,21 +2114,23 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { .into_iter() .collect_vec(); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([fs_device.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([fs_device.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([fs_device.clone()]) - .unwrap(), - ) - .unwrap(); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([fs_device.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([fs_device.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([fs_device.clone()]) + // .unwrap(), + // ) + // .unwrap(); - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + + let shield_0 = SecurityStructureOfFactorSources::sample(); let (_, derivation_outcome) = os .__OFFLINE_ONLY_securify_accounts( @@ -2292,30 +2309,32 @@ async fn securified_accounts_asymmetric_indices() { .into_iter() .collect_vec(); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::threshold_factors_only( - [fs_device.clone()], - 1, - ) - .unwrap(), - RecoveryRoleWithFactorSources::threshold_factors_only( - [fs_device.clone()], - 1, - ) - .unwrap(), - ConfirmationRoleWithFactorSources::threshold_factors_only( - [fs_device.clone()], - 1, - ) - .unwrap(), - ) - .unwrap(); - - let shield_0 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - 14, - matrix_0, - ); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::threshold_factors_only( + // [fs_device.clone()], + // 1, + // ) + // .unwrap(), + // RecoveryRoleWithFactorSources::threshold_factors_only( + // [fs_device.clone()], + // 1, + // ) + // .unwrap(), + // ConfirmationRoleWithFactorSources::threshold_factors_only( + // [fs_device.clone()], + // 1, + // ) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_0 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), + // 14, + // matrix_0, + // ); + + let shield_0 = SecurityStructureOfFactorSources::sample(); let (_, derivation_outcome) = os .__OFFLINE_ONLY_securify_accounts( @@ -2406,30 +2425,32 @@ async fn securified_accounts_asymmetric_indices() { 4 ); - let matrix_1 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_arculus.clone(), - ]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_arculus.clone(), - ]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_arculus.clone(), - ]) - .unwrap(), - ) - .unwrap(); - - let shield_1 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), - 14, - matrix_1, - ); + // let matrix_1 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_arculus.clone(), + // ]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_arculus.clone(), + // ]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_arculus.clone(), + // ]) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_1 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), + // 14, + // matrix_1, + // ); + + let shield_1 = SecurityStructureOfFactorSources::sample_other(); let (securified_alice, derivation_outcome) = os .__OFFLINE_ONLY_securify_account(alice.address(), &shield_1) @@ -2471,30 +2492,32 @@ async fn securified_accounts_asymmetric_indices() { .collect::>() ); - let matrix_2 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_ledger.clone(), - ]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_ledger.clone(), - ]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_ledger.clone(), - ]) - .unwrap(), - ) - .unwrap(); - - let shield_2 = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 2").unwrap()), - 14, - matrix_2, - ); + // let matrix_2 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_ledger.clone(), + // ]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_ledger.clone(), + // ]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_ledger.clone(), + // ]) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_2 = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 2").unwrap()), + // 14, + // matrix_2, + // ); + + let shield_2 = SecurityStructureOfFactorSources::sample(); let (securified_bob, derivation_outcome) = os .__OFFLINE_ONLY_securify_account(bob.address(), &shield_2) @@ -2578,33 +2601,35 @@ async fn securified_accounts_asymmetric_indices() { // CLEAR CACHE os.clear_cache().await; - let matrix_3fa = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_arculus.clone(), - fs_ledger.clone(), - ]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_arculus.clone(), - fs_ledger.clone(), - ]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([ - fs_device.clone(), - fs_arculus.clone(), - fs_ledger.clone(), - ]) - .unwrap(), - ) - .unwrap(); - - let shield_3fa = SecurityStructureOfFactorSources::new( - SecurityStructureMetadata::new(DisplayName::new("Shield 3fa").unwrap()), - 14, - matrix_3fa, - ); + // let matrix_3fa = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_arculus.clone(), + // fs_ledger.clone(), + // ]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_arculus.clone(), + // fs_ledger.clone(), + // ]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([ + // fs_device.clone(), + // fs_arculus.clone(), + // fs_ledger.clone(), + // ]) + // .unwrap(), + // ) + // .unwrap(); + + // let shield_3fa = SecurityStructureOfFactorSources::new( + // SecurityStructureMetadata::new(DisplayName::new("Shield 3fa").unwrap()), + // 14, + // matrix_3fa, + // ); + + let shield_3fa = SecurityStructureOfFactorSources::sample(); let (securified_diana, derivation_outcome) = os .__OFFLINE_ONLY_securify_account(diana.address(), &shield_3fa) diff --git a/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs b/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs index 68a92bfd4..ed729a875 100644 --- a/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs +++ b/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs @@ -151,94 +151,97 @@ mod tests { #[should_panic] #[actix_rt::test] async fn mfa_panics_if_entities_empty() { - let fs = FactorSource::sample_at(0); - let a = Account::sample(); - let cache_client = FactorInstancesCacheClient::in_memory(); - - let _ = SUT::for_account_mfa( - Arc::new(cache_client), - Arc::new(Profile::sample_from([fs.clone()], [&a], [])), - MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ) - .unwrap(), - IndexSet::::new(), // <---- EMPTY => should_panic - Arc::new(TestDerivationInteractors::default()), - ) - .await - .unwrap(); + // let fs = FactorSource::sample_at(0); + // let a = Account::sample(); + // let cache_client = FactorInstancesCacheClient::in_memory(); + + // let _ = SUT::for_account_mfa( + // Arc::new(cache_client), + // Arc::new(Profile::sample_from([fs.clone()], [&a], [])), + // MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ) + // .unwrap(), + // IndexSet::::new(), // <---- EMPTY => should_panic + // Arc::new(TestDerivationInteractors::default()), + // ) + // .await + // .unwrap(); + todo!() } #[should_panic] #[actix_rt::test] async fn mfa_panics_if_entity_unknown() { - let fs = FactorSource::sample_at(0); - let a = Account::sample(); - let cache_client = FactorInstancesCacheClient::in_memory(); - - let _ = SUT::for_account_mfa( - Arc::new(cache_client), - Arc::new(Profile::sample_from([fs.clone()], [&a], [])), - MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ) - .unwrap(), - IndexSet::just(Account::sample_other().address()), // <---- unknown => should_panic - Arc::new(TestDerivationInteractors::default()), - ) - .await - .unwrap(); + // let fs = FactorSource::sample_at(0); + // let a = Account::sample(); + // let cache_client = FactorInstancesCacheClient::in_memory(); + + // let _ = SUT::for_account_mfa( + // Arc::new(cache_client), + // Arc::new(Profile::sample_from([fs.clone()], [&a], [])), + // MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ) + // .unwrap(), + // IndexSet::just(Account::sample_other().address()), // <---- unknown => should_panic + // Arc::new(TestDerivationInteractors::default()), + // ) + // .await + // .unwrap(); + todo!() } #[should_panic(expected = "Missing FactorSources")] #[actix_rt::test] async fn mfa_panics_if_factor_source_missing() { - let fs = FactorSource::sample_at(0); - let network = NetworkID::Mainnet; - - let mainnet_account = Account::new(HDFactorInstanceTransactionSigning::new(HierarchicalDeterministicFactorInstance::new_for_entity_on_network( - network, - fs.id_from_hash(), - CAP26EntityKind::Account, - Hardened::Unsecurified( - UnsecurifiedHardened::try_from(0u32).unwrap(), - ), - )).unwrap(), DisplayName::sample(), AppearanceID::sample()); - - let profile = Profile::sample_from( - [], // <---- missing factor source => should_panic - [&mainnet_account], - [], - ); - let cache_client = FactorInstancesCacheClient::in_memory(); - - let _ = SUT::for_account_mfa( - Arc::new(cache_client), - Arc::new(profile), - MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ) - .unwrap(), - IndexSet::from_iter([mainnet_account.address()]), - Arc::new(TestDerivationInteractors::default()), - ) - .await - .unwrap(); + // let fs = FactorSource::sample_at(0); + // let network = NetworkID::Mainnet; + + // let mainnet_account = Account::new(HDFactorInstanceTransactionSigning::new(HierarchicalDeterministicFactorInstance::new_for_entity_on_network( + // network, + // fs.id_from_hash(), + // CAP26EntityKind::Account, + // Hardened::Unsecurified( + // UnsecurifiedHardened::try_from(0u32).unwrap(), + // ), + // )).unwrap(), DisplayName::sample(), AppearanceID::sample()); + + // let profile = Profile::sample_from( + // [], // <---- missing factor source => should_panic + // [&mainnet_account], + // [], + // ); + // let cache_client = FactorInstancesCacheClient::in_memory(); + + // let _ = SUT::for_account_mfa( + // Arc::new(cache_client), + // Arc::new(profile), + // MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ) + // .unwrap(), + // IndexSet::from_iter([mainnet_account.address()]), + // Arc::new(TestDerivationInteractors::default()), + // ) + // .await + // .unwrap(); + todo!() } #[should_panic] @@ -273,29 +276,30 @@ mod tests { [], ); - assert_eq!(profile.networks.len(), 2); - let cache_client = FactorInstancesCacheClient::in_memory(); - - let _ = SUT::for_account_mfa( - Arc::new(cache_client), - Arc::new(profile), - MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - .unwrap(), - ) - .unwrap(), - IndexSet::from_iter([ - mainnet_account.address(), - stokenet_account.address(), - ]), // <---- wrong network => should_panic - Arc::new(TestDerivationInteractors::default()), - ) - .await - .unwrap(); + // assert_eq!(profile.networks.len(), 2); + // let cache_client = FactorInstancesCacheClient::in_memory(); + + // let _ = SUT::for_account_mfa( + // Arc::new(cache_client), + // Arc::new(profile), + // MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) + // .unwrap(), + // ) + // .unwrap(), + // IndexSet::from_iter([ + // mainnet_account.address(), + // stokenet_account.address(), + // ]), // <---- wrong network => should_panic + // Arc::new(TestDerivationInteractors::default()), + // ) + // .await + // .unwrap(); + todo!() } #[actix_rt::test] @@ -322,49 +326,50 @@ mod tests { .unwrap(); assert!(derivation_outcome.debug_was_derived.is_empty()); - let matrix_0 = MatrixOfFactorSources::new( - PrimaryRoleWithFactorSources::override_only([bdfs.clone()]) - .unwrap(), - RecoveryRoleWithFactorSources::override_only([bdfs.clone()]) - .unwrap(), - ConfirmationRoleWithFactorSources::override_only([bdfs.clone()]) - .unwrap(), - ) - .unwrap(); - - let cache_client = Arc::new(os.clients.factor_instances_cache.clone()); - let profile = Arc::new(os.profile().unwrap()); - let derivation_interactors = os.keys_derivation_interactors(); - - let (instances_in_cache_consumer, outcome) = SUT::for_account_mfa( - cache_client.clone(), - profile, - matrix_0.clone(), - IndexSet::just(alice.address()), - derivation_interactors.clone(), - ) - .await - .unwrap(); - - // don't forget to consume - instances_in_cache_consumer.consume().await.unwrap(); - let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); - assert_eq!(outcome.to_use_directly.len(), 1); - - let profile = Arc::new(os.profile().unwrap()); - let (instances_in_cache_consumer, outcome) = SUT::for_persona_mfa( - cache_client.clone(), - profile, - matrix_0.clone(), - IndexSet::just(batman.address()), - derivation_interactors.clone(), - ) - .await - .unwrap(); - - // don't forget to consume - instances_in_cache_consumer.consume().await.unwrap(); - let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); - assert_eq!(outcome.to_use_directly.len(), 1); + // let matrix_0 = MatrixOfFactorSources::new( + // PrimaryRoleWithFactorSources::override_only([bdfs.clone()]) + // .unwrap(), + // RecoveryRoleWithFactorSources::override_only([bdfs.clone()]) + // .unwrap(), + // ConfirmationRoleWithFactorSources::override_only([bdfs.clone()]) + // .unwrap(), + // ) + // .unwrap(); + + // let cache_client = Arc::new(os.clients.factor_instances_cache.clone()); + // let profile = Arc::new(os.profile().unwrap()); + // let derivation_interactors = os.keys_derivation_interactors(); + + // let (instances_in_cache_consumer, outcome) = SUT::for_account_mfa( + // cache_client.clone(), + // profile, + // matrix_0.clone(), + // IndexSet::just(alice.address()), + // derivation_interactors.clone(), + // ) + // .await + // .unwrap(); + + // // don't forget to consume + // instances_in_cache_consumer.consume().await.unwrap(); + // let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); + // assert_eq!(outcome.to_use_directly.len(), 1); + + // let profile = Arc::new(os.profile().unwrap()); + // let (instances_in_cache_consumer, outcome) = SUT::for_persona_mfa( + // cache_client.clone(), + // profile, + // matrix_0.clone(), + // IndexSet::just(batman.address()), + // derivation_interactors.clone(), + // ) + // .await + // .unwrap(); + + // // don't forget to consume + // instances_in_cache_consumer.consume().await.unwrap(); + // let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); + // assert_eq!(outcome.to_use_directly.len(), 1); + todo!() } } diff --git a/crates/sargon/src/lib.rs b/crates/sargon/src/lib.rs index 139d6da3d..660e8cc71 100644 --- a/crates/sargon/src/lib.rs +++ b/crates/sargon/src/lib.rs @@ -5,6 +5,8 @@ #![allow(internal_features)] #![feature(iter_repeat_n)] #![feature(future_join)] +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] mod core; mod factor_instances_provider; @@ -33,10 +35,10 @@ pub mod prelude { pub use crate::system::*; pub use crate::types::*; pub use crate::wrapped_radix_engine_toolkit::*; - pub use radix_rust::prelude::{ indexmap, BTreeSet, HashMap, HashSet, IndexMap, IndexSet, }; + pub(crate) use std::marker::PhantomData; pub(crate) use ::hex::decode as hex_decode; pub(crate) use ::hex::encode as hex_encode; diff --git a/crates/sargon/src/profile/logic/account/query_security_structures.rs b/crates/sargon/src/profile/logic/account/query_security_structures.rs index 8de8e671a..dad7be110 100644 --- a/crates/sargon/src/profile/logic/account/query_security_structures.rs +++ b/crates/sargon/src/profile/logic/account/query_security_structures.rs @@ -1,5 +1,23 @@ use crate::prelude::*; +decl_identified_vec_of!( + /// A collection of [`SecurityStructureOfFactorSources`] + SecurityStructuresOfFactorSources, + SecurityStructureOfFactorSources +); + +impl HasSampleValues for SecurityStructuresOfFactorSources { + fn sample() -> Self { + Self::from_iter([ + SecurityStructureOfFactorSources::sample(), + SecurityStructureOfFactorSources::sample_other(), + ]) + } + fn sample_other() -> Self { + Self::from_iter([SecurityStructureOfFactorSources::sample_other()]) + } +} + impl Profile { /// Returns all the SecurityStructuresOfFactorSources, /// by trying to map FactorSourceID level -> FactorSource Level diff --git a/crates/sargon/src/profile/mfa/security_structures/decl_security_structure_of.rs b/crates/sargon/src/profile/mfa/security_structures/decl_security_structure_of.rs deleted file mode 100644 index 1fd6a7483..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/decl_security_structure_of.rs +++ /dev/null @@ -1,378 +0,0 @@ -use crate::prelude::*; - -macro_rules! decl_role_with_factors_additional_impl { - ( - $role: ident, - FactorInstance - ) => { - paste! { - impl From<[< $role RoleWithFactorInstance s >]> for ScryptoAccessRule { - fn from(value: [< $role RoleWithFactorInstance s >]) -> Self { - ScryptoAccessRule::Protected(ScryptoCompositeRequirement::AnyOf(vec![ - ScryptoCompositeRequirement::BasicRequirement(ScryptoBasicRequirement::CountOf( - value.threshold, - value - .threshold_factors - .into_iter() - .map(|instance| instance.badge) - .map(ScryptoResourceOrNonFungible::from) - .collect(), - )), - ScryptoCompositeRequirement::BasicRequirement(ScryptoBasicRequirement::AnyOf( - value - .override_factors - .into_iter() - .map(|instance| instance.badge) - .map(ScryptoResourceOrNonFungible::from) - .collect(), - )), - ])) - } - } - } - }; - ( - $role: ident, - $factor: ident - ) => {} -} -pub(crate) use decl_role_with_factors_additional_impl; - -macro_rules! decl_role_with_factors_with_role_kind_attrs { - ( - $( - #[doc = $expr: expr] - )* - $role: ident, - $factor: ident, - $($extra_field_name:ident: $extra_field_type:ty,)* - ) => { - paste! { - $( - #[doc = $expr] - )* - #[derive( - Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, - )] - #[serde(rename_all = "camelCase")] - pub struct [< $role RoleWith $factor s >] { - - /// Factors which are used in combination with other instances, amounting to at - /// least `threshold` many instances to perform some function with this role. - /// - /// # Implementation - /// Must allow duplicates, thus using `Vec` since at FactorSourceKind level - /// we might wanna use duplicates, allowing us to build a "template" - /// structure where a role might contain two `FactorSourceKind::TrustedContact`, - /// meaning an instance of this template at FactorSource level - /// (`SecurityStructureOfFactorSources`) will contain two different - /// `TrustedContactFactorSource`s. - pub threshold_factors: Vec<$factor>, - - /// How many threshold factors that must be used to perform some function with this role. - pub threshold: u8, - - /// Overriding / Super admin / "sudo" / God / factors, **ANY** - /// single of these factor which can perform the function of this role, - /// disregarding of `threshold`. - pub override_factors: Vec<$factor>, - - $(pub $extra_field_name: $extra_field_type,)* - } - - - impl RoleWithFactors<$factor> for [< $role RoleWith $factor s >] { - - fn get_threshold_factors(&self) -> &Vec<$factor> { - &self.threshold_factors - } - - fn get_threshold(&self) -> u8 { - self.threshold - } - - fn get_override_factors(&self) -> &Vec<$factor> { - &self.override_factors - } - } - - impl [< $role RoleWith $factor s >] { - - pub fn unique_factors(&self) -> IndexSet<$factor> { - self.all_factors().into_iter().map(|x| x.clone()).collect() - } - - // # Panics - /// Panics if `threshold > threshold_factor.len()` - /// - /// Panics if the same factor is present in both lists - /// - /// Panics if Factor elements are FactorInstances and the derivation - /// path contains a non-securified last path component. - pub fn with_factors_and_role( - $($extra_field_name: $extra_field_type,)* - threshold_factors: impl IntoIterator, - threshold: u8, - override_factors: impl IntoIterator, - ) -> Result { - - let assert_is_securified = |factors: &Vec::<$factor>| -> Result<()> { - let trait_objects: Vec<&dyn IsMaybeKeySpaceAware> = factors.iter().map(|x| x as &dyn IsMaybeKeySpaceAware).collect(); - if trait_objects.iter() - .filter_map(|x| x.maybe_key_space()) - .any(|x| x != KeySpace::Securified) { - return Err(crate::CommonError::IndexUnsecurifiedExpectedSecurified) - } - Ok(()) - }; - - - let threshold_factors = threshold_factors.into_iter().collect_vec(); - - if threshold_factors.len() < threshold as usize { - return Err(CommonError::InvalidSecurityStructureThresholdExceedsFactors { - threshold, - factors: threshold_factors.len() as u8 - }) - } - - let override_factors = override_factors.into_iter().collect_vec(); - - assert_is_securified(&threshold_factors)?; - assert_is_securified(&override_factors)?; - - if !HashSet::<$factor>::from_iter(threshold_factors.clone()) - .intersection(&HashSet::<$factor>::from_iter(override_factors.clone())) - .collect_vec() - .is_empty() { - return Err(CommonError::InvalidSecurityStructureFactorInBothThresholdAndOverride) - } - - Ok(Self { - threshold_factors, - threshold, - override_factors, - $($extra_field_name,)* - }) - } - } - } - }; -} - -pub(crate) use decl_role_with_factors_with_role_kind_attrs; - -macro_rules! decl_role_with_factors { - ( - $( - #[doc = $expr: expr] - )* - $role: ident, - $factor: ident - ) => { - - decl_role_with_factors_with_role_kind_attrs!( - $( - #[doc = $expr] - )* - $role, - $factor, - ); - - paste! { - - impl [< $role RoleWith $factor s >] { - - pub fn new( - threshold_factors: impl IntoIterator, - threshold: u8, - override_factors: impl IntoIterator - ) -> Result { - Self::with_factors_and_role(threshold_factors, threshold, override_factors) - } - - - /// # Panics - /// Panics if `threshold > factors.len()` - /// - /// Panics if Factor elements are FactorInstances and the derivation - /// path contains a non-securified last path component. - pub fn threshold_factors_only( - factors: impl IntoIterator, - threshold: u8, - ) -> Result { - Self::new(factors, threshold, []) - } - - /// # Panics - /// Panics if Factor elements are FactorInstances and the derivation - /// path contains a non-securified last path component. - pub fn override_only( - factors: impl IntoIterator, - ) -> Result { - Self::new([], 0, factors) - } - } - } - - decl_role_with_factors_additional_impl!($role, $factor); - }; -} - -pub(crate) use decl_role_with_factors; - -macro_rules! decl_role_runtime_kind_with_factors { - ( - $( - #[doc = $expr: expr] - )* - $role: ident, - $factor: ident - ) => { - decl_role_with_factors_with_role_kind_attrs!( - $( - #[doc = $expr] - )* - $role, - $factor, - role: RoleKind, - ); - }; -} - -pub(crate) use decl_role_runtime_kind_with_factors; - -macro_rules! decl_matrix_of_factors { - ( - $( - #[doc = $expr: expr] - )* - $factor: ident - ) => { - paste! { - - decl_role_with_factors!( - /// PrimaryRole is used for Signing Transactions. - Primary, - $factor - ); - - decl_role_with_factors!( - /// RecoveryRole is used to recover lost access to an entity. - Recovery, - $factor - ); - - decl_role_with_factors!( - /// ConfirmationRole is used to confirm recovery. - Confirmation, - $factor - ); - - $( - #[doc = $expr] - )* - #[derive( - Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, - )] - #[serde(rename_all = "camelCase")] - pub struct [< MatrixOf $factor s >] { - - /// Used for Signing transactions - pub primary_role: [< PrimaryRoleWith $factor s >], - - /// Used to initiate recovery - resetting the used Security Shield - /// of an entity. - pub recovery_role: [< RecoveryRoleWith $factor s >], - - /// To confirm recovery. - pub confirmation_role: [< ConfirmationRoleWith $factor s >], - } - - impl [< MatrixOf $factor s >] { - pub fn new( - primary_role: [< PrimaryRoleWith $factor s >], - recovery_role: [< RecoveryRoleWith $factor s >], - confirmation_role: [< ConfirmationRoleWith $factor s >], - ) -> Result { - Ok(Self { - primary_role, - recovery_role, - confirmation_role, - }) - } - - pub fn all_factors(&self) -> HashSet<&$factor> { - let mut factors = HashSet::new(); - factors.extend(self.primary_role.all_factors()); - factors.extend(self.recovery_role.all_factors()); - factors.extend(self.confirmation_role.all_factors()); - factors - } - - pub fn get_role_of_kind(&self, role_kind: RoleKind) -> &dyn RoleWithFactors<$factor> { - match role_kind { - RoleKind::Confirmation => &self.confirmation_role, - RoleKind::Primary => &self.primary_role, - RoleKind::Recovery => &self.recovery_role, - } - } - } - } - }; -} - -pub(crate) use decl_matrix_of_factors; - -macro_rules! decl_security_structure_of { - ( - $( - #[doc = $expr: expr] - )* - $factor: ident, - ) => { - - decl_matrix_of_factors!($factor); - - paste! { - - $( - #[doc = $expr] - )* - #[derive( - Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, - )] - #[serde(rename_all = "camelCase")] - pub struct [< SecurityStructureOf $factor s >] { - /// Metadata of this Security Structure, such as globally unique and - /// stable identifier, creation date and user chosen label (name). - pub metadata: SecurityStructureMetadata, - - /// The amount of time until Confirmation Role is automatically - /// exercised, inputted by user in Days in UI, but translate it into - /// epochs ("block time"). - pub number_of_epochs_until_auto_confirmation: u64, - - /// The structure of factors to use for certain roles, Primary, Recovery - /// and Confirmation role. - pub matrix_of_factors: [< MatrixOf $factor s >], - } - - impl [< SecurityStructureOf $factor s >] { - pub fn new(metadata: SecurityStructureMetadata, number_of_epochs_until_auto_confirmation: u64, matrix_of_factors: [< MatrixOf $factor s >]) -> Self { - Self { - metadata, - number_of_epochs_until_auto_confirmation, - matrix_of_factors - } - } - - pub fn all_factors(&self) -> HashSet<&$factor> { - self.matrix_of_factors.all_factors() - } - } - } - }; -} - -pub(crate) use decl_security_structure_of; diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/confirmation_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/confirmation_role_with_factor_instances.rs deleted file mode 100644 index 9b31cf218..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/confirmation_role_with_factor_instances.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::prelude::*; - -impl HasRoleKind for ConfirmationRoleWithFactorInstances { - fn role_kind() -> RoleKind { - RoleKind::Confirmation - } -} - -impl HasFactorInstances for ConfirmationRoleWithFactorInstances { - fn unique_factor_instances(&self) -> IndexSet { - self.unique_factors() - } -} - -impl HasSampleValues for ConfirmationRoleWithFactorInstances { - fn sample() -> Self { - Self::new([HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(27).into()], 1, [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(13).into()]) - .unwrap() - } - - fn sample_other() -> Self { - Self::new( - [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(6).into(), HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(42).into()], - 2, - [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(19).into()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = ConfirmationRoleWithFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/matrix_of_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/matrix_of_factor_instances.rs deleted file mode 100644 index f00f61ba8..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/matrix_of_factor_instances.rs +++ /dev/null @@ -1,202 +0,0 @@ -use sbor::prelude::indexmap::IndexSet; - -use crate::prelude::*; - -impl HasFactorInstances for MatrixOfFactorInstances { - fn unique_factor_instances(&self) -> IndexSet { - let mut set = IndexSet::new(); - set.extend(self.primary_role.all_factors().into_iter().cloned()); - set.extend(self.recovery_role.all_factors().into_iter().cloned()); - set.extend(self.confirmation_role.all_factors().into_iter().cloned()); - set - } -} - -impl HasSampleValues for MatrixOfFactorInstances { - fn sample() -> Self { - Self::new( - PrimaryRoleWithFactorInstances::sample(), - RecoveryRoleWithFactorInstances::sample(), - ConfirmationRoleWithFactorInstances::sample(), - ) - .unwrap() - } - - fn sample_other() -> Self { - Self::new( - PrimaryRoleWithFactorInstances::sample_other(), - RecoveryRoleWithFactorInstances::sample_other(), - ConfirmationRoleWithFactorInstances::sample_other(), - ) - .unwrap() - } -} - -impl MatrixOfFactorInstances { - /// Maps `MatrixOfFactorSources -> MatrixOfFactorInstances` by - /// "assigning" FactorInstances to each MatrixOfFactorInstances from - /// `consuming_instances`. - /// - /// NOTE: - /// **One FactorInstance might be used multiple times in the MatrixOfFactorInstances, - /// e.g. ones in the PrimaryRole(WithFactorInstances) and again in RecoveryRole(WithFactorInstances) or - /// in RecoveryRole(WithFactorInstances)**. - /// - /// However, the same FactorInstance is NEVER used in two different MatrixOfFactorInstances. - /// - /// - pub fn fulfilling_matrix_of_factor_sources_with_instances( - consuming_instances: &mut IndexMap< - FactorSourceIDFromHash, - FactorInstances, - >, - matrix_of_factor_sources: MatrixOfFactorSources, - ) -> Result { - let instances = &consuming_instances.clone(); - - let primary = fulfilling_role_of_factor_sources_with_factor_instances( - instances, - &matrix_of_factor_sources, - PrimaryRoleWithFactorInstances::new, - )?; - let recovery = fulfilling_role_of_factor_sources_with_factor_instances( - instances, - &matrix_of_factor_sources, - RecoveryRoleWithFactorInstances::new, - )?; - let confirmation = - fulfilling_role_of_factor_sources_with_factor_instances( - instances, - &matrix_of_factor_sources, - ConfirmationRoleWithFactorInstances::new, - )?; - - let matrix = Self::new(primary, recovery, confirmation)?; - - // Now that we have assigned instances, **possibly the SAME INSTANCE to multiple roles**, - // lets delete them from the `consuming_instances` map. - for instance in matrix.all_factors() { - let fsid = - &FactorSourceIDFromHash::try_from(instance.factor_source_id) - .unwrap(); - let existing = consuming_instances.get_mut(fsid).unwrap(); - - let to_remove = HierarchicalDeterministicFactorInstance::try_from( - instance.clone(), - ) - .unwrap(); - - // We remove at the beginning of the list first. - existing.shift_remove(&to_remove); - - if existing.is_empty() { - // not needed per se, but feels prudent to "prune". - consuming_instances.shift_remove_entry(fsid); - } - } - - Ok(matrix) - } -} - -// TODO: MFA - Upgrade this method to follow the rules of when a factor instance might -// be used by MULTIPLE roles. This is a temporary solution to get the tests to pass. -// A proper solution should use follow the rules laid out in: -// https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields -fn fulfilling_role_of_factor_sources_with_factor_instances< - U: HasRoleKind + RoleWithFactors, ->( - consuming_instances: &IndexMap, - matrix_of_factor_sources: &MatrixOfFactorSources, - make_role: impl FnOnce( - Vec, - u8, - Vec, - ) -> Result, -) -> Result { - let role_kind = U::role_kind(); - let role_of_sources = matrix_of_factor_sources.get_role_of_kind(role_kind); - let threshold: u8 = role_of_sources.get_threshold(); - - // Threshold factors - let ti = - try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( - consuming_instances, - role_of_sources.get_threshold_factors() - )?; - - // Override factors - let oi = - try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( - consuming_instances, - role_of_sources.get_override_factors() - )?; - - let role = make_role(ti, threshold, oi)?; - - assert_eq!(role.get_role_kind(), role_kind); - Ok(role) -} - -fn try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( - instances: &IndexMap, - from: &[FactorSource], -) -> Result> { - from.iter() - .map(|f| { - if let Some(existing) = instances.get(&f.id_from_hash()) { - let hd_instance = existing.first().ok_or( - CommonError::MissingFactorMappingInstancesIntoRole, - )?; - let instance = FactorInstance::from(hd_instance); - Ok(instance) - } else { - Err(CommonError::MissingFactorMappingInstancesIntoRole) - } - }) - .collect::>>() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = MatrixOfFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - fn err_if_no_instance_found_for_factor_source() { - assert!(matches!( - SUT::fulfilling_matrix_of_factor_sources_with_instances( - &mut IndexMap::new(), - MatrixOfFactorSources::sample() - ), - Err(CommonError::MissingFactorMappingInstancesIntoRole) - )); - } - - #[test] - fn err_if_empty_instance_found_for_factor_source() { - assert!(matches!( - SUT::fulfilling_matrix_of_factor_sources_with_instances( - &mut IndexMap::kv( - FactorSource::sample_device_babylon().id_from_hash(), - FactorInstances::from_iter([]) - ), - MatrixOfFactorSources::sample() - ), - Err(CommonError::MissingFactorMappingInstancesIntoRole) - )); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/mod.rs deleted file mode 100644 index c0ef2eff0..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod confirmation_role_with_factor_instances; -mod matrix_of_factor_instances; -mod primary_role_with_factor_instances; -mod recovery_role_with_factor_instances; -mod security_structure_of_factor_instances; - -pub use confirmation_role_with_factor_instances::*; -pub use matrix_of_factor_instances::*; -pub use primary_role_with_factor_instances::*; -pub use recovery_role_with_factor_instances::*; -pub use security_structure_of_factor_instances::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/primary_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/primary_role_with_factor_instances.rs deleted file mode 100644 index 304760d0c..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/primary_role_with_factor_instances.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::prelude::*; - -impl HasRoleKind for PrimaryRoleWithFactorInstances { - fn role_kind() -> RoleKind { - RoleKind::Primary - } -} - -impl HasFactorInstances for PrimaryRoleWithFactorInstances { - fn unique_factor_instances(&self) -> IndexSet { - self.unique_factors() - } -} - -impl HasSampleValues for PrimaryRoleWithFactorInstances { - fn sample() -> Self { - Self::new([HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(0).into()], 1, [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(0).into()]) - .unwrap() - } - - fn sample_other() -> Self { - Self::new( - [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(1).into(), - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(11).into(), - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(12).into()], - 2, - [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(6).into()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrimaryRoleWithFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - fn primary_role_non_securified_threshold_instances_is_err() { - assert!(matches!( - SUT::threshold_factors_only( - [ - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_unsecurified_at_index(0).into() - ], - 1, - ), - Err(CommonError::IndexUnsecurifiedExpectedSecurified) - )); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/recovery_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/recovery_role_with_factor_instances.rs deleted file mode 100644 index 01d195d03..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/recovery_role_with_factor_instances.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::prelude::*; - -impl HasRoleKind for RecoveryRoleWithFactorInstances { - fn role_kind() -> RoleKind { - RoleKind::Recovery - } -} - -impl HasFactorInstances for RecoveryRoleWithFactorInstances { - fn unique_factor_instances(&self) -> IndexSet { - self.unique_factors() - } -} - -impl HasSampleValues for RecoveryRoleWithFactorInstances { - fn sample() -> Self { - Self::new([HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(54).into()], 1, [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(237).into()]) - .unwrap() - } - - fn sample_other() -> Self { - Self::new( - [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(65).into(), - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_1_securified_at_index(25).into()], - 2, - [HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_securified_at_index(31).into()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = RecoveryRoleWithFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/security_structure_of_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/security_structure_of_factor_instances.rs deleted file mode 100644 index 0ec4b1a80..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_instance_level/security_structure_of_factor_instances.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::prelude::*; - -decl_matrix_of_factors!( - /// A matrix of FactorInstances - FactorInstance -); - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] -#[serde(rename_all = "camelCase")] -pub struct SecurityStructureOfFactorInstances { - /// The ID of the `SecurityStructureOfFactorSourceIDs` in - /// `profile.app_preferences.security.security_structures_of_factor_source_ids` - /// which was used to derive the factor instances in this structure. Or rather: - /// The id of `SecurityStructureOfFactorSources`. - pub security_structure_id: SecurityStructureID, - - /// The structure of factors to use for certain roles, Primary, Recovery - /// and Confirmation role. - pub matrix_of_factors: MatrixOfFactorInstances, -} - -impl SecurityStructureOfFactorInstances { - pub fn new( - security_structure_id: SecurityStructureID, - matrix_of_factors: MatrixOfFactorInstances, - ) -> Self { - Self { - security_structure_id, - matrix_of_factors, - } - } -} - -impl Identifiable for SecurityStructureOfFactorInstances { - type ID = ::ID; - - fn id(&self) -> Self::ID { - self.security_structure_id - } -} - -impl HasSampleValues for SecurityStructureOfFactorInstances { - fn sample() -> Self { - Self { - security_structure_id: SecurityStructureID::sample(), - matrix_of_factors: MatrixOfFactorInstances::sample(), - } - } - - fn sample_other() -> Self { - Self { - security_structure_id: SecurityStructureID::sample_other(), - matrix_of_factors: MatrixOfFactorInstances::sample_other(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = SecurityStructureOfFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/confirmation_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/confirmation_role_with_factor_source_ids.rs deleted file mode 100644 index 7b8e4d912..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/confirmation_role_with_factor_source_ids.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::prelude::*; - -impl From - for ConfirmationRoleWithFactorSourceIDs -{ - fn from(value: ConfirmationRoleWithFactorSources) -> Self { - Self::new( - value.threshold_factors.iter().map(|x| x.factor_source_id()), - value.threshold, - value.override_factors.iter().map(|x| x.factor_source_id()), - ) - .expect("ConfirmationRoleWithFactorSources has already been validated.") - } -} - -impl HasSampleValues for ConfirmationRoleWithFactorSourceIDs { - fn sample() -> Self { - Self::new( - [FactorSourceID::sample()], - 1, - [FactorSourceID::sample_other()], - ) - .unwrap() - } - fn sample_other() -> Self { - Self::new( - [FactorSourceID::sample_other()], - 0, - [FactorSourceID::sample()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = ConfirmationRoleWithFactorSourceIDs; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/matrix_of_factor_source_ids.rs deleted file mode 100644 index 900dd75ba..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/matrix_of_factor_source_ids.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::prelude::*; - -impl From for MatrixOfFactorSourceIDs { - fn from(value: MatrixOfFactorSources) -> Self { - Self::new( - value.primary_role.into(), - value.recovery_role.into(), - value.confirmation_role.into(), - ) - .unwrap() - } -} - -impl HasSampleValues for MatrixOfFactorSourceIDs { - fn sample() -> Self { - Self::new( - PrimaryRoleWithFactorSourceIDs::sample(), - RecoveryRoleWithFactorSourceIDs::sample(), - ConfirmationRoleWithFactorSourceIDs::sample(), - ) - .unwrap() - } - fn sample_other() -> Self { - Self::new( - PrimaryRoleWithFactorSourceIDs::sample_other(), - RecoveryRoleWithFactorSourceIDs::sample_other(), - ConfirmationRoleWithFactorSourceIDs::sample_other(), - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = MatrixOfFactorSourceIDs; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/mod.rs deleted file mode 100644 index a7b0f2d88..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod confirmation_role_with_factor_source_ids; -mod matrix_of_factor_source_ids; -mod primary_role_with_factor_source_ids; -mod recovery_role_with_factor_source_ids; -mod security_structure_id; -mod security_structure_of_factor_source_ids; -mod security_structures_of_factor_source_ids; - -pub use confirmation_role_with_factor_source_ids::*; -pub use matrix_of_factor_source_ids::*; -pub use primary_role_with_factor_source_ids::*; -pub use recovery_role_with_factor_source_ids::*; -pub use security_structure_id::*; -pub use security_structure_of_factor_source_ids::*; -pub use security_structures_of_factor_source_ids::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/primary_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/primary_role_with_factor_source_ids.rs deleted file mode 100644 index a96ce8a5d..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/primary_role_with_factor_source_ids.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::prelude::*; - -impl From for PrimaryRoleWithFactorSourceIDs { - fn from(value: PrimaryRoleWithFactorSources) -> Self { - Self::new( - value.threshold_factors.iter().map(|x| x.factor_source_id()), - value.threshold, - value.override_factors.iter().map(|x| x.factor_source_id()), - ) - .expect("PrimaryRoleWithFactorSources has already been validated.") - } -} - -impl HasSampleValues for PrimaryRoleWithFactorSourceIDs { - fn sample() -> Self { - Self::threshold_factors_only( - [FactorSourceID::sample(), FactorSourceID::sample_other()], - 2, - ) - .unwrap() - } - fn sample_other() -> Self { - Self::new( - [FactorSourceID::sample()], - 1, - [FactorSourceID::sample_other()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrimaryRoleWithFactorSourceIDs; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/recovery_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/recovery_role_with_factor_source_ids.rs deleted file mode 100644 index 2c777f9ee..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/recovery_role_with_factor_source_ids.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::prelude::*; - -impl From for RecoveryRoleWithFactorSourceIDs { - fn from(value: RecoveryRoleWithFactorSources) -> Self { - Self::new( - value.threshold_factors.iter().map(|x| x.factor_source_id()), - value.threshold, - value.override_factors.iter().map(|x| x.factor_source_id()), - ) - .expect("RecoveryRoleWithFactorSources has already been validated.") - } -} - -impl HasSampleValues for RecoveryRoleWithFactorSourceIDs { - fn sample() -> Self { - Self::threshold_factors_only([FactorSourceID::sample_other()], 1) - .unwrap() - } - fn sample_other() -> Self { - Self::new( - [FactorSourceID::sample()], - 1, - [FactorSourceID::sample_other()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = RecoveryRoleWithFactorSourceIDs; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structure_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structure_of_factor_source_ids.rs deleted file mode 100644 index 9c4904a8a..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structure_of_factor_source_ids.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::prelude::*; - -decl_security_structure_of!( - /// A security structure at FactorSourceID level, this is - /// what is serialized and store into Profile, we convert - /// into this structure from `SecurityStructureOfFactorSources`. - FactorSourceID, -); - -impl Identifiable for SecurityStructureOfFactorSourceIDs { - type ID = ::ID; - - fn id(&self) -> Self::ID { - self.metadata.id() - } -} - -impl From - for SecurityStructureOfFactorSourceIDs -{ - fn from(value: SecurityStructureOfFactorSources) -> Self { - Self::new( - value.metadata, - value.number_of_epochs_until_auto_confirmation, - value.matrix_of_factors.into(), - ) - } -} - -impl HasSampleValues for SecurityStructureOfFactorSourceIDs { - fn sample() -> Self { - SecurityStructureOfFactorSources::sample().into() - } - fn sample_other() -> Self { - SecurityStructureOfFactorSources::sample_other().into() - } -} - -#[cfg(test)] -mod test_schematic_of_security_shield { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = SecurityStructureOfFactorSourceIDs; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structures_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structures_of_factor_source_ids.rs deleted file mode 100644 index 0796f9bb0..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structures_of_factor_source_ids.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::prelude::*; - -decl_identified_vec_of!( - /// A collection of [`SecurityStructureOfFactorSourceIDs`] - SecurityStructuresOfFactorSourceIDs, - SecurityStructureOfFactorSourceIDs -); - -impl HasSampleValues for SecurityStructuresOfFactorSourceIDs { - fn sample() -> Self { - Self::from_iter([ - SecurityStructureOfFactorSourceIDs::sample(), - SecurityStructureOfFactorSourceIDs::sample_other(), - ]) - } - fn sample_other() -> Self { - Self::from_iter([SecurityStructureOfFactorSourceIDs::sample_other()]) - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/matrix_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_level/matrix_of_factor_sources.rs deleted file mode 100644 index 442a959fe..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/matrix_of_factor_sources.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::prelude::*; - -impl HasSampleValues for MatrixOfFactorSources { - fn sample() -> Self { - Self::new( - PrimaryRoleWithFactorSources::sample(), - RecoveryRoleWithFactorSources::sample(), - ConfirmationRoleWithFactorSources::sample(), - ) - .unwrap() - } - fn sample_other() -> Self { - Self::new( - PrimaryRoleWithFactorSources::sample_other(), - RecoveryRoleWithFactorSources::sample_other(), - ConfirmationRoleWithFactorSources::sample_other(), - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = MatrixOfFactorSources; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_level/mod.rs deleted file mode 100644 index ddaa2484a..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod confirmation_role_with_factor_sources; -mod matrix_of_factor_sources; -mod primary_role_with_factor_sources; -mod recovery_role_with_factor_sources; -mod security_structure_of_factor_sources; -mod security_structures_of_factor_sources; - -pub use confirmation_role_with_factor_sources::*; -pub use primary_role_with_factor_sources::*; -pub use recovery_role_with_factor_sources::*; -pub use security_structure_of_factor_sources::*; -pub use security_structures_of_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/primary_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_level/primary_role_with_factor_sources.rs deleted file mode 100644 index a4e542727..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/primary_role_with_factor_sources.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::prelude::*; - -impl HasSampleValues for PrimaryRoleWithFactorSources { - fn sample() -> Self { - Self::new( - [ - FactorSource::sample_device_babylon(), - FactorSource::sample_arculus(), - FactorSource::sample_off_device(), - ], - 2, - [FactorSource::sample_ledger()], - ) - .unwrap() - } - fn sample_other() -> Self { - Self::new( - [ - FactorSource::sample_device_babylon_other(), - FactorSource::sample_arculus_other(), - FactorSource::sample_off_device_other(), - ], - 2, - [FactorSource::sample_ledger_other()], - ) - .unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrimaryRoleWithFactorSources; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structure_of_factor_sources.rs deleted file mode 100644 index 1a221f115..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structure_of_factor_sources.rs +++ /dev/null @@ -1,213 +0,0 @@ -use crate::prelude::*; - -decl_security_structure_of!( - /// Security structure at `FactorSource` level. - /// This is what user view, creates and manages. - /// - /// Before it gets saved into Profile gets converted into - /// `SecurityStructureOfFactorSourceIDs` - FactorSource, -); - -pub const MINUTES_PER_DAY: u64 = 24 * 60; -pub const MINUTES_PER_EPOCH: u64 = 5; -pub const EPOCHS_PER_DAY: u64 = MINUTES_PER_DAY / MINUTES_PER_EPOCH; - -pub fn days_to_epochs(days: u16) -> u64 { - let days = days as u64; - days * EPOCHS_PER_DAY -} - -impl SecurityStructureOfFactorSources { - pub fn new_with_days( - metadata: SecurityStructureMetadata, - number_of_days_until_auto_confirmation: u16, - matrix_of_factors: MatrixOfFactorSources, - ) -> Self { - Self::new( - metadata, - days_to_epochs(number_of_days_until_auto_confirmation), - matrix_of_factors, - ) - } -} - -impl Identifiable for SecurityStructureOfFactorSources { - type ID = ::ID; - - fn id(&self) -> Self::ID { - self.metadata.id() - } -} - -fn factors_from( - ids: &[FactorSourceID], - from: &FactorSources, -) -> Result { - ids.iter() - .map(|id| { - from.get_id(*id) - .ok_or(CommonError::ProfileDoesNotContainFactorSourceWithID { - bad_value: *id, - }) - .cloned() - }) - .collect::>() -} - -impl TryFrom<(&PrimaryRoleWithFactorSourceIDs, &FactorSources)> - for PrimaryRoleWithFactorSources -{ - type Error = CommonError; - fn try_from( - value: (&PrimaryRoleWithFactorSourceIDs, &FactorSources), - ) -> Result { - let (id_level, factor_sources) = value; - - let threshold_factors = - factors_from(&id_level.threshold_factors, factor_sources)?; - - let override_factors = - factors_from(&id_level.override_factors, factor_sources)?; - Self::new(threshold_factors, id_level.threshold, override_factors) - } -} - -impl TryFrom<(&RecoveryRoleWithFactorSourceIDs, &FactorSources)> - for RecoveryRoleWithFactorSources -{ - type Error = CommonError; - fn try_from( - value: (&RecoveryRoleWithFactorSourceIDs, &FactorSources), - ) -> Result { - let (id_level, factor_sources) = value; - - let threshold_factors = - factors_from(&id_level.threshold_factors, factor_sources)?; - - let override_factors = - factors_from(&id_level.override_factors, factor_sources)?; - Self::new(threshold_factors, id_level.threshold, override_factors) - } -} - -impl TryFrom<(&ConfirmationRoleWithFactorSourceIDs, &FactorSources)> - for ConfirmationRoleWithFactorSources -{ - type Error = CommonError; - fn try_from( - value: (&ConfirmationRoleWithFactorSourceIDs, &FactorSources), - ) -> Result { - let (id_level, factor_sources) = value; - - let threshold_factors = - factors_from(&id_level.threshold_factors, factor_sources)?; - - let override_factors = - factors_from(&id_level.override_factors, factor_sources)?; - Self::new(threshold_factors, id_level.threshold, override_factors) - } -} - -impl TryFrom<(&MatrixOfFactorSourceIDs, &FactorSources)> - for MatrixOfFactorSources -{ - type Error = CommonError; - fn try_from( - value: (&MatrixOfFactorSourceIDs, &FactorSources), - ) -> Result { - let (id_level, factor_sources) = value; - let primary_role = PrimaryRoleWithFactorSources::try_from(( - &id_level.primary_role, - factor_sources, - ))?; - - let recovery_role = RecoveryRoleWithFactorSources::try_from(( - &id_level.recovery_role, - factor_sources, - ))?; - - let confirmation_role = ConfirmationRoleWithFactorSources::try_from(( - &id_level.confirmation_role, - factor_sources, - ))?; - - Self::new(primary_role, recovery_role, confirmation_role) - } -} - -impl TryFrom<(&SecurityStructureOfFactorSourceIDs, &FactorSources)> - for SecurityStructureOfFactorSources -{ - type Error = CommonError; - fn try_from( - value: (&SecurityStructureOfFactorSourceIDs, &FactorSources), - ) -> Result { - let (id_level, factor_sources) = value; - let matrix = MatrixOfFactorSources::try_from(( - &id_level.matrix_of_factors, - factor_sources, - ))?; - Ok(Self::new( - id_level.metadata.clone(), - id_level.number_of_epochs_until_auto_confirmation, - matrix, - )) - } -} - -impl HasSampleValues for SecurityStructureOfFactorSources { - fn sample() -> Self { - Self::new_with_days( - SecurityStructureMetadata::sample(), - 14, - MatrixOfFactorSources::sample(), - ) - } - fn sample_other() -> Self { - Self::new_with_days( - SecurityStructureMetadata::sample_other(), - 28, - MatrixOfFactorSources::sample_other(), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = SecurityStructureOfFactorSources; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - fn test_epochs_per_day() { - assert_eq!(EPOCHS_PER_DAY, 288); - } - - #[test] - fn test_days_to_epochs() { - assert_eq!(days_to_epochs(0), 0); - assert_eq!(days_to_epochs(10), 2880); - } - - #[test] - fn test_into_id_level_and_back() { - let factor_sources = FactorSources::sample_values_all(); - let sut = SUT::sample(); - let id_level = SecurityStructureOfFactorSourceIDs::from(sut.clone()); - let detailed = SUT::try_from((&id_level, &factor_sources)).unwrap(); - assert_eq!(detailed, sut); - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structures_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structures_of_factor_sources.rs deleted file mode 100644 index d64ef90fc..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/security_structures_of_factor_sources.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::prelude::*; - -decl_identified_vec_of!( - /// A collection of [`SecurityStructureOfFactorSources`] - SecurityStructuresOfFactorSources, - SecurityStructureOfFactorSources -); - -impl HasSampleValues for SecurityStructuresOfFactorSources { - fn sample() -> Self { - Self::from_iter([ - SecurityStructureOfFactorSources::sample(), - SecurityStructureOfFactorSources::sample_other(), - ]) - } - fn sample_other() -> Self { - Self::from_iter([SecurityStructureOfFactorSources::sample_other()]) - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs new file mode 100644 index 000000000..f2d7b2779 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs @@ -0,0 +1,50 @@ +use crate::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AbstractMatrixBuilderOrBuilt { + #[serde(skip)] + #[doc(hidden)] + pub(crate) built: PhantomData, + + pub(crate) primary_role: AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, F, U>, + pub(crate) recovery_role: + AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, F, U>, + pub(crate) confirmation_role: + AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, F, U>, + + pub(crate) number_of_days_until_auto_confirm: u16, +} +impl AbstractMatrixBuilderOrBuilt { + pub const DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM: u16 = 14; +} + +pub type AbstractMatrixBuilt = AbstractMatrixBuilderOrBuilt; + +impl AbstractMatrixBuilt { + pub fn primary(&self) -> &AbstractBuiltRoleWithFactor<{ ROLE_PRIMARY }, F> { + &self.primary_role + } + + pub fn recovery( + &self, + ) -> &AbstractBuiltRoleWithFactor<{ ROLE_RECOVERY }, F> { + &self.recovery_role + } + + pub fn confirmation( + &self, + ) -> &AbstractBuiltRoleWithFactor<{ ROLE_CONFIRMATION }, F> { + &self.confirmation_role + } +} + +impl AbstractMatrixBuilt { + pub fn all_factors(&self) -> HashSet<&F> { + let mut factors = HashSet::new(); + factors.extend(self.primary_role.all_factors()); + factors.extend(self.recovery_role.all_factors()); + factors.extend(self.confirmation_role.all_factors()); + factors + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs new file mode 100644 index 000000000..e7a181792 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs @@ -0,0 +1,64 @@ +use crate::prelude::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum MatrixRolesInCombinationViolation { + #[error("Basic violation: {0}")] + Basic(#[from] MatrixRolesInCombinationBasicViolation), + + #[error("Forever invalid: {0}")] + ForeverInvalid(#[from] MatrixRolesInCombinationForeverInvalid), + + #[error("Not yet valid: {0}")] + NotYetValid(#[from] MatrixRolesInCombinationNotYetValid), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum MatrixRolesInCombinationBasicViolation { + #[error("The factor source was not found in any role")] + FactorSourceNotFoundInAnyRole, + + #[error("The number of days until auto confirm must be greater than zero")] + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum MatrixRolesInCombinationForeverInvalid { + #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] + RecoveryAndConfirmationFactorsOverlap, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum MatrixRolesInCombinationNotYetValid { + #[error("The single factor used in the primary role must not be used in any other role")] + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum MatrixBuilderValidation { + #[error("Role {role:?} in isolation violation: {violation}")] + RoleInIsolation { + role: RoleKind, + violation: RoleBuilderValidation, + }, + #[error("Roles in combination violation: {0}")] + CombinationViolation(#[from] MatrixRolesInCombinationViolation), +} + +pub(crate) trait IntoMatrixErr { + fn into_matrix_err( + self, + role: RoleKind, + ) -> Result; +} + +impl IntoMatrixErr for Result { + fn into_matrix_err( + self, + role: RoleKind, + ) -> Result { + self.map_err(|violation| MatrixBuilderValidation::RoleInIsolation { + role, + violation, + }) + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs new file mode 100644 index 000000000..dc3e814c0 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -0,0 +1,376 @@ +#![allow(clippy::new_without_default)] + +use crate::prelude::*; + +pub type MatrixBuilderMutateResult = Result<(), MatrixBuilderValidation>; +pub type MatrixBuilderBuildResult = + Result; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Built; + +pub type MatrixBuilder = AbstractMatrixBuilderOrBuilt< + FactorSourceID, + MatrixOfFactorSourceIds, + Built, // this is HACKY +>; + +// ================== +// ===== PUBLIC ===== +// ================== +impl MatrixBuilder { + pub fn new() -> Self { + Self { + built: PhantomData, + primary_role: PrimaryRoleBuilder::new(), + recovery_role: RecoveryRoleBuilder::new(), + confirmation_role: ConfirmationRoleBuilder::new(), + number_of_days_until_auto_confirm: + Self::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + } + } + + pub fn build(self) -> MatrixBuilderBuildResult { + self.validate_combination()?; + + let primary = self + .primary_role + .build() + .into_matrix_err(RoleKind::Primary)?; + let recovery = self + .recovery_role + .build() + .into_matrix_err(RoleKind::Recovery)?; + let confirmation = self + .confirmation_role + .build() + .into_matrix_err(RoleKind::Confirmation)?; + + let built = MatrixOfFactorSourceIds { + built: PhantomData, + primary_role: primary, + recovery_role: recovery, + confirmation_role: confirmation, + number_of_days_until_auto_confirm: self + .number_of_days_until_auto_confirm, + }; + Ok(built) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.primary_role + .validation_for_addition_of_factor_source_of_kind_to_threshold( + factor_source_kind, + ) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_primary_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.primary_role + .validation_for_addition_of_factor_source_of_kind_to_override( + factor_source_kind, + ) + } + + pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &self, + factor_sources: &IndexSet, + ) -> IndexSet { + self.primary_role + .validation_for_addition_of_factor_source_for_each( + FactorListKind::Threshold, + factor_sources, + ) + } + + pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( + &self, + factor_sources: &IndexSet, + ) -> IndexSet { + self.primary_role + .validation_for_addition_of_factor_source_for_each( + FactorListKind::Override, + factor_sources, + ) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_recovery_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.recovery_role + .validation_for_addition_of_factor_source_of_kind_to_override( + factor_source_kind, + ) + } + + pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( + &self, + factor_sources: &IndexSet, + ) -> IndexSet { + self.recovery_role + .validation_for_addition_of_factor_source_for_each( + FactorListKind::Override, + factor_sources, + ) + } + + pub fn validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.confirmation_role + .validation_for_addition_of_factor_source_of_kind_to_override( + factor_source_kind, + ) + } + + pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( + &self, + factor_sources: &IndexSet, + ) -> IndexSet { + self.confirmation_role + .validation_for_addition_of_factor_source_for_each( + FactorListKind::Override, + factor_sources, + ) + } + + pub fn validate_each_role_in_isolation(&self) -> MatrixBuilderMutateResult { + self.primary_role + .validate() + .into_matrix_err(RoleKind::Primary)?; + self.recovery_role + .validate() + .into_matrix_err(RoleKind::Recovery)?; + self.confirmation_role + .validate() + .into_matrix_err(RoleKind::Confirmation)?; + Ok(()) + } + + pub fn validate(&self) -> MatrixBuilderMutateResult { + self.validate_each_role_in_isolation()?; + self.validate_combination()?; + Ok(()) + } + + /// Adds the factor source to the primary role threshold list. + pub fn add_factor_source_to_primary_threshold( + &mut self, + factor_source_id: FactorSourceID, + ) -> MatrixBuilderMutateResult { + self.primary_role + .add_factor_source_to_threshold(factor_source_id) + .into_matrix_err(RoleKind::Primary) + } + + /// Adds the factor source to the primary role override list. + pub fn add_factor_source_to_primary_override( + &mut self, + factor_source_id: FactorSourceID, + ) -> MatrixBuilderMutateResult { + self.primary_role + .add_factor_source_to_override(factor_source_id) + .into_matrix_err(RoleKind::Primary) + } + + pub fn add_factor_source_to_recovery_override( + &mut self, + factor_source_id: FactorSourceID, + ) -> MatrixBuilderMutateResult { + self.recovery_role + .add_factor_source_to_override(factor_source_id) + .into_matrix_err(RoleKind::Recovery) + } + + pub fn add_factor_source_to_confirmation_override( + &mut self, + factor_source_id: FactorSourceID, + ) -> MatrixBuilderMutateResult { + self.confirmation_role + .add_factor_source_to_override(factor_source_id) + .into_matrix_err(RoleKind::Confirmation) + } + + pub fn get_confirmation_factors(&self) -> &Vec { + self.confirmation_role.get_override_factors() + } + + pub fn get_recovery_factors(&self) -> &Vec { + self.recovery_role.get_override_factors() + } + + pub fn get_primary_threshold_factors(&self) -> &Vec { + self.primary_role.get_threshold_factors() + } + + pub fn get_primary_override_factors(&self) -> &Vec { + self.primary_role.get_override_factors() + } + + /// Sets the threshold on the primary role builder. + pub fn set_threshold( + &mut self, + threshold: u8, + ) -> MatrixBuilderMutateResult { + self.primary_role + .set_threshold(threshold) + .into_matrix_err(RoleKind::Primary) + } + + pub fn get_threshold(&self) -> u8 { + self.primary_role.get_threshold() + } + + pub fn set_number_of_days_until_auto_confirm( + &mut self, + number_of_days: u16, + ) -> MatrixBuilderMutateResult { + self.number_of_days_until_auto_confirm = number_of_days; + + self.validate_number_of_days_until_auto_confirm() + } + + pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { + self.number_of_days_until_auto_confirm + } + + /// Removes `factor_source_id` from all three roles, if not found in any an error + /// is thrown. + /// + /// # Throws + /// If none of the three role builders contains the factor source id, `Err(BasicViolation::FactorSourceNotFound)` is thrown + pub fn remove_factor( + &mut self, + factor_source_id: &FactorSourceID, + ) -> MatrixBuilderMutateResult { + let mut found = false; + if self + .primary_role + .remove_factor_source(factor_source_id) + .is_ok() + { + found = true; + } + if self + .recovery_role + .remove_factor_source(factor_source_id) + .is_ok() + { + found = true; + } + if self + .confirmation_role + .remove_factor_source(factor_source_id) + .is_ok() + { + found = true; + } + if !found { + MatrixBuilderMutateResult::Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::Basic( + MatrixRolesInCombinationBasicViolation::FactorSourceNotFoundInAnyRole, + ), + )) + } else { + Ok(()) + } + } +} + +// ================== +// ==== PRIVATE ===== +// ================== +impl MatrixBuilder { + fn validate_if_primary_has_single_it_must_not_be_used_by_any_other_role( + &self, + ) -> MatrixBuilderMutateResult { + let primary_has_single_factor = + self.primary_role.all_factors().len() == 1; + if primary_has_single_factor { + let primary_factors = self.primary_role.all_factors(); + let primary_factor = primary_factors.first().unwrap(); + let recovery_set = HashSet::<_>::from_iter( + self.recovery_role.get_override_factors(), + ); + let confirmation_set = HashSet::<_>::from_iter( + self.confirmation_role.get_override_factors(), + ); + if recovery_set.contains(primary_factor) + || confirmation_set.contains(primary_factor) + { + return Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::NotYetValid(MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole), + )); + } + } + Ok(()) + } + + fn validate_no_factor_may_be_used_in_both_recovery_and_confirmation( + &self, + ) -> MatrixBuilderMutateResult { + let recovery_set = + HashSet::<_>::from_iter(self.recovery_role.get_override_factors()); + let confirmation_set = HashSet::<_>::from_iter( + self.confirmation_role.get_override_factors(), + ); + let intersection = recovery_set + .intersection(&confirmation_set) + .collect::>(); + if intersection.is_empty() { + Ok(()) + } else { + Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::ForeverInvalid( + MatrixRolesInCombinationForeverInvalid::RecoveryAndConfirmationFactorsOverlap, + ), + )) + } + } + + fn validate_number_of_days_until_auto_confirm( + &self, + ) -> MatrixBuilderMutateResult { + if self.number_of_days_until_auto_confirm == 0 { + return Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::Basic( + MatrixRolesInCombinationBasicViolation::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero, + ), + )); + } + Ok(()) + } + + /// Security Shield Rules + /// In addition to the factor/role rules above, the wallet must enforce certain rules for combinations of + /// factors across the three roles. The construction method described in the next section will automatically + /// always follow these rules. A user may however choose to manually add/remove factors from their Shield + /// configuration and so the wallet must evaluate these rules and inform the user when the combination they + /// have chosen cannot be used. The wallet should never allow a user to complete a Shield configuration that + /// violates these rules. + /// + /// 1. If only one factor is used for `Primary`, that factor may not be used for either `Recovery` or `Confirmation` + /// 2. No factor may be used (override) in both `Recovery` and `Confirmation` + /// 3. No factor may be used in both the `Primary` threshold and `Primary` override + /// 4. Number of days until auto confirm is greater than zero + fn validate_combination(&self) -> MatrixBuilderMutateResult { + self.validate_if_primary_has_single_it_must_not_be_used_by_any_other_role()?; + self.validate_no_factor_may_be_used_in_both_recovery_and_confirmation( + )?; + + // N.B. the third 3: + // "3. No factor may be used in both the `Primary` threshold and `Primary` override" + // is already enforced by the RoleBuilder + + self.validate_number_of_days_until_auto_confirm()?; + + Ok(()) + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs new file mode 100644 index 000000000..b1508e6f4 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs @@ -0,0 +1,1976 @@ +#![cfg(test)] +use crate::prelude::*; + +#[allow(clippy::upper_case_acronyms)] +type SUT = MatrixBuilder; + +fn make() -> SUT { + SUT::new() +} + +#[test] +fn empty_primary_is_err() { + let sut = make(); + let res = sut.build(); + assert_eq!( + res, + MatrixBuilderBuildResult::Err( + MatrixBuilderValidation::RoleInIsolation { + role: RoleKind::Primary, + violation: RoleBuilderValidation::NotYetValid( + NotYetValidReason::RoleMustHaveAtLeastOneFactor + ) + } + ) + ) +} + +#[test] +fn empty_recovery_is_err() { + let mut sut = make(); + sut.add_factor_source_to_primary_override(FactorSourceID::sample_ledger()) + .unwrap(); + let res = sut.build(); + assert_eq!( + res, + MatrixBuilderBuildResult::Err( + MatrixBuilderValidation::RoleInIsolation { + role: RoleKind::Recovery, + violation: RoleBuilderValidation::NotYetValid( + NotYetValidReason::RoleMustHaveAtLeastOneFactor + ) + } + ) + ) +} + +#[test] +fn empty_confirmation_is_err() { + let mut sut = make(); + sut.add_factor_source_to_primary_override(FactorSourceID::sample_ledger()) + .unwrap(); + + sut.add_factor_source_to_recovery_override(FactorSourceID::sample_arculus()) + .unwrap(); + let res = sut.build(); + assert_eq!( + res, + MatrixBuilderBuildResult::Err( + MatrixBuilderValidation::RoleInIsolation { + role: RoleKind::Confirmation, + violation: RoleBuilderValidation::NotYetValid( + NotYetValidReason::RoleMustHaveAtLeastOneFactor + ) + } + ) + ) +} + +#[test] +fn set_number_of_days_cannot_be_zero() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold(FactorSourceID::sample_device()) + .unwrap(); + + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override(FactorSourceID::sample_ledger()) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + sut.number_of_days_until_auto_confirm = 0; // bypass validation + + // Build + let validation = MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::Basic(MatrixRolesInCombinationBasicViolation::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) + ); + assert_eq!(sut.validate(), Err(validation)); + let res = sut.build(); + assert_eq!(res, Err(validation)); +} + +#[test] +fn set_number_of_days_42() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold(FactorSourceID::sample_device()) + .unwrap(); + + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override(FactorSourceID::sample_ledger()) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + sut.set_number_of_days_until_auto_confirm(42).unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles_and_days( + RoleWithFactorSourceIds::primary_with_factors( + 1, + [FactorSourceID::sample_device(),], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + 42, + ) + ); +} + +#[test] +fn auto_confirm_default() { + assert_eq!(SUT::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, 14); +} + +#[test] +fn set_number_of_days_if_not_set_uses_default() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold(FactorSourceID::sample_device()) + .unwrap(); + + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override(FactorSourceID::sample_ledger()) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles_and_days( + RoleWithFactorSourceIds::primary_with_factors( + 1, + [FactorSourceID::sample_device(),], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + SUT::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + ) + ); +} + +#[test] +fn sample_factor_cannot_be_both_in_threshold_and_override() { + let mut sut = make(); + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_override(fs).unwrap(); + let res = sut.add_factor_source_to_primary_override(fs); + assert!(res.is_err()); +} + +#[test] +fn single_factor_in_primary_threshold_cannot_be_in_recovery() { + let mut sut = make(); + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_threshold(fs).unwrap(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus_other(), + ) + .unwrap(); + sut.set_threshold(1).unwrap(); + + // ACT + sut.add_factor_source_to_recovery_override(fs).unwrap(); + let res = sut.validate(); + assert_eq!(res, Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::NotYetValid(MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole) + ))); + + sut.add_factor_source_to_primary_threshold(FactorSourceID::sample_arculus()) + .unwrap(); + + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built.primary(), + &RoleWithFactorSourceIds::primary_with_factors( + 1, + [ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_arculus() + ], + [] + ) + ); + pretty_assertions::assert_eq!( + built.recovery(), + &RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger() + ]), + ); + + pretty_assertions::assert_eq!( + built.confirmation(), + &RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_arculus_other() + ]) + ) +} + +#[test] +fn single_factor_in_primary_override_cannot_be_in_recovery() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + + // ACT + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_override(fs).unwrap(); + sut.add_factor_source_to_recovery_override(fs).unwrap(); + + // ASSERT + let res = sut.validate(); + assert_eq!(res, Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::NotYetValid(MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole) + ))); +} + +#[test] +fn single_factor_in_primary_threshold_cannot_be_in_confirmation() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_recovery_override(FactorSourceID::sample_arculus()) + .unwrap(); + _ = sut.set_threshold(1); + + // ACT + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_threshold(fs).unwrap(); + sut.add_factor_source_to_confirmation_override(fs).unwrap(); + + // ASSERT + let res = sut.validate(); + assert_eq!(res, Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::NotYetValid(MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole) + ))); +} + +#[test] +fn single_factor_in_primary_override_cannot_be_in_confirmation() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_recovery_override(FactorSourceID::sample_arculus()) + .unwrap(); + + // ACT + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_override(fs).unwrap(); + sut.add_factor_source_to_confirmation_override(fs).unwrap(); + + // ASSERT + let res = sut.validate(); + assert_eq!(res, Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::NotYetValid(MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole) + ))); +} + +#[test] +fn add_factor_to_recovery_then_same_to_confirmation_is_err() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override(FactorSourceID::sample_arculus()) + .unwrap(); + + // ACT + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_confirmation_override(fs).unwrap(); + sut.add_factor_source_to_recovery_override(fs).unwrap(); + + // ASSERT + let res = sut.validate(); + assert_eq!( + res, + Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::ForeverInvalid( + MatrixRolesInCombinationForeverInvalid::RecoveryAndConfirmationFactorsOverlap + ) + )) + ); +} + +#[test] +fn add_factor_to_confirmation_then_same_to_override_when_validated_is_err() { + // ARRANGE + let mut sut = make(); + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_override(FactorSourceID::sample_arculus()) + .unwrap(); + + // ACT + sut.add_factor_source_to_recovery_override(fs).unwrap(); + sut.add_factor_source_to_confirmation_override(fs).unwrap(); + + // ASSERT + let res = sut.validate(); + assert_eq!( + res, + Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::ForeverInvalid( + MatrixRolesInCombinationForeverInvalid::RecoveryAndConfirmationFactorsOverlap + ) + )) + ); +} + +#[test] +fn add_factor_to_confirmation_then_same_to_primary_threshold_is_not_yet_valid() +{ + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + _ = sut.set_threshold(1); + + // ACT + let fs = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_recovery_override(fs).unwrap(); + sut.add_factor_source_to_primary_threshold(fs).unwrap(); + + // ASSERT + let res = sut.validate(); + assert_eq!(res, Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::NotYetValid(MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole) + ))); +} + +mod remove { + use super::*; + + #[test] + fn not_found() { + let mut sut = make(); + let res = sut.remove_factor(&FactorSourceID::sample_device()); + assert_eq!( + res, + Err(MatrixBuilderValidation::CombinationViolation( + MatrixRolesInCombinationViolation::Basic( + MatrixRolesInCombinationBasicViolation::FactorSourceNotFoundInAnyRole + ) + )) + ); + } + + #[test] + fn remove_from_primary_threshold_is_ok() { + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.remove_factor(&FactorSourceID::sample_device()); + assert_eq!(res, Ok(())); + } + + #[test] + fn remove_from_primary_override_is_ok() { + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.remove_factor(&FactorSourceID::sample_device()); + assert_eq!(res, Ok(())); + } + + #[test] + fn remove_from_recovery_is_ok() { + let mut sut = make(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.remove_factor(&FactorSourceID::sample_device()); + assert_eq!(res, Ok(())); + } + + #[test] + fn remove_from_confirmation_is_ok() { + let mut sut = make(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.remove_factor(&FactorSourceID::sample_device()); + assert_eq!(res, Ok(())); + } +} + +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( + RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + 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( + RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + 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( + RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + 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( + RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + 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( + RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + 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( + RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + 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( + RoleKind::Primary, + FactorSourceID::sample_device() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + 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( + RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Primary, + FactorSourceID::sample_device_other(), + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + ), + ] + ); + } + } + + mod recovery { + use super::*; + + fn role() -> RoleKind { + RoleKind::Recovery + } + + #[test] + fn empty() { + let sut = make(); + let xs = sut.validation_for_addition_of_factor_source_to_recovery_override_for_each( + &IndexSet::new(), + ); + assert_eq!(xs, IndexSet::new()); + } + + #[test] + fn supported() { + let sut = make(); + let fsids = vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other(), + FactorSourceID::sample_arculus(), + FactorSourceID::sample_arculus_other(), + FactorSourceID::sample_off_device(), + FactorSourceID::sample_off_device_other(), + FactorSourceID::sample_trusted_contact(), + FactorSourceID::sample_trusted_contact_other(), + ]; + let xs = sut.validation_for_addition_of_factor_source_to_recovery_override_for_each( + &IndexSet::from_iter(fsids.clone()), + ); + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + fsids + .into_iter() + .map(|fsid| FactorSourceInRoleBuilderValidationStatus::ok( + role(), + fsid + )) + .collect::>() + ); + } + + #[test] + fn password_and_security_questions_not_supported() { + let sut = make(); + let xs = sut.validation_for_addition_of_factor_source_to_recovery_override_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_security_questions(), + FactorSourceID::sample_security_questions_other(), + ]), + ); + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + [ + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_security_questions(), + FactorSourceID::sample_security_questions_other(), + ] + .into_iter() + .map( + |fsid| FactorSourceInRoleBuilderValidationStatus::forever_invalid( + role(), + fsid, + if fsid.get_factor_source_kind() == FactorSourceKind::SecurityQuestions { + ForeverInvalidReason::RecoveryRoleSecurityQuestionsNotSupported + } else { + ForeverInvalidReason::RecoveryRolePasswordNotSupported + } + ) + ) + .collect::>() + ); + } + } + + mod confirmation { + use super::*; + + fn role() -> RoleKind { + RoleKind::Confirmation + } + + #[test] + fn empty() { + let sut = make(); + let xs = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each( + &IndexSet::new(), + ); + assert_eq!(xs, IndexSet::new()); + } + + #[test] + fn supported() { + let sut = make(); + let fsids = vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other(), + FactorSourceID::sample_arculus(), + FactorSourceID::sample_arculus_other(), + FactorSourceID::sample_security_questions(), + FactorSourceID::sample_security_questions_other(), + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_off_device(), + FactorSourceID::sample_off_device_other(), + ]; + let xs = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each( + &IndexSet::from_iter(fsids.clone()), + ); + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + fsids + .into_iter() + .map(|fsid| FactorSourceInRoleBuilderValidationStatus::ok( + role(), + fsid + )) + .collect::>() + ); + } + + #[test] + fn password_and_security_questions_not_supported() { + let sut = make(); + let xs = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each( + &IndexSet::from_iter([ + FactorSourceID::sample_trusted_contact(), + FactorSourceID::sample_trusted_contact_other(), + ]), + ); + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + [ + FactorSourceID::sample_trusted_contact(), + FactorSourceID::sample_trusted_contact_other(), + ] + .into_iter() + .map( + |fsid| FactorSourceInRoleBuilderValidationStatus::forever_invalid( + role(), + fsid, + ForeverInvalidReason::ConfirmationRoleTrustedContactNotSupported + ) + ) + .collect::>() + ); + } + } +} + +mod validation_of_addition_of_kind { + use super::*; + + mod recovery { + use super::*; + + #[test] + fn validation_for_addition_of_factor_source_of_kind_to_recovery_override_empty( + ) { + let sut = make(); + let test = |kind: FactorSourceKind, should_be_ok: bool| { + let is_ok = sut + .validation_for_addition_of_factor_source_of_kind_to_recovery_override(kind) + .is_ok(); + assert_eq!(is_ok, should_be_ok); + }; + test(FactorSourceKind::Device, true); + test(FactorSourceKind::LedgerHQHardwareWallet, true); + test(FactorSourceKind::ArculusCard, true); + test(FactorSourceKind::SecurityQuestions, false); + test(FactorSourceKind::Password, false); + test(FactorSourceKind::OffDeviceMnemonic, true); + test(FactorSourceKind::TrustedContact, true); + } + + #[test] + fn validation_for_addition_of_factor_source_of_kind_to_recovery_override_single_recovery( + ) { + let mut sut = make(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + let test = |kind: FactorSourceKind, should_be_ok: bool| { + let is_ok = sut + .validation_for_addition_of_factor_source_of_kind_to_recovery_override(kind) + .is_ok(); + assert_eq!(is_ok, should_be_ok); + }; + test(FactorSourceKind::Device, true); + test(FactorSourceKind::LedgerHQHardwareWallet, true); + test(FactorSourceKind::ArculusCard, true); + test(FactorSourceKind::SecurityQuestions, false); + test(FactorSourceKind::Password, false); + test(FactorSourceKind::OffDeviceMnemonic, true); + test(FactorSourceKind::TrustedContact, true); + } + } + + mod confirmation { + use super::*; + + #[test] + fn validation_for_addition_of_factor_source_of_kind_to_confirmation_override_empty( + ) { + let sut = make(); + let test = |kind: FactorSourceKind, should_be_ok: bool| { + let is_ok = sut + .validation_for_addition_of_factor_source_of_kind_to_confirmation_override(kind) + .is_ok(); + assert_eq!(is_ok, should_be_ok); + }; + test(FactorSourceKind::Device, true); + test(FactorSourceKind::LedgerHQHardwareWallet, true); + test(FactorSourceKind::ArculusCard, true); + test(FactorSourceKind::SecurityQuestions, true); + test(FactorSourceKind::Password, true); + test(FactorSourceKind::OffDeviceMnemonic, true); + test(FactorSourceKind::TrustedContact, false); + } + + #[test] + fn validation_for_addition_of_factor_source_of_kind_to_confirmation_override_single_recovery( + ) { + let mut sut = make(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + let test = |kind: FactorSourceKind, should_be_ok: bool| { + let is_ok = sut + .validation_for_addition_of_factor_source_of_kind_to_confirmation_override(kind) + .is_ok(); + assert_eq!(is_ok, should_be_ok); + }; + test(FactorSourceKind::Device, true); + test(FactorSourceKind::LedgerHQHardwareWallet, true); + test(FactorSourceKind::ArculusCard, true); + test(FactorSourceKind::SecurityQuestions, true); + test(FactorSourceKind::Password, true); + test(FactorSourceKind::OffDeviceMnemonic, true); + test(FactorSourceKind::TrustedContact, false); + } + } + + mod primary { + use super::*; + + #[test] + fn ledger_threshold_threshold() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::LedgerHQHardwareWallet, + ); + assert!(res.is_ok()); + } + + #[test] + fn ledger_threshold_override() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::LedgerHQHardwareWallet, + ); + assert!(res.is_ok()); + } + + #[test] + fn ledger_override_override() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::LedgerHQHardwareWallet, + ); + assert!(res.is_ok()); + } + + #[test] + fn ledger_override_threshold() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::LedgerHQHardwareWallet, + ); + assert!(res.is_ok()); + } + + #[test] + fn arculus_threshold_threshold() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::ArculusCard, + ); + assert!(res.is_ok()); + } + + #[test] + fn arculus_threshold_override() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::ArculusCard, + ); + assert!(res.is_ok()); + } + + #[test] + fn arculus_override_override() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::ArculusCard, + ); + assert!(res.is_ok()); + } + + #[test] + fn arculus_override_threshold() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::ArculusCard, + ); + assert!(res.is_ok()); + } + + #[test] + fn security_questions_not_supported_threshold() { + // ARRANGE + let sut = make(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::SecurityQuestions, + ); + assert!(res.is_err()); + } + + #[test] + fn security_questions_not_supported_override() { + // ARRANGE + let sut = make(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::SecurityQuestions, + ); + assert!(res.is_err()); + } + + #[test] + fn trusted_contact_not_supported_threshold() { + // ARRANGE + let sut = make(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::TrustedContact, + ); + assert!(res.is_err()); + } + + #[test] + fn trusted_contact_not_supported_override() { + // ARRANGE + let sut = make(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::TrustedContact, + ); + assert!(res.is_err()); + } + + #[test] + fn passphrase_threshold_threshold() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_off_device(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::OffDeviceMnemonic, + ); + assert!(res.is_ok()); + } + + #[test] + fn passphrase_threshold_override() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_off_device(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::OffDeviceMnemonic, + ); + assert!(res.is_ok()); + } + + #[test] + fn passphrase_override_override() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_off_device(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::OffDeviceMnemonic, + ); + assert!(res.is_ok()); + } + + #[test] + fn passphrase_override_threshold() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_off_device(), + ) + .unwrap(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::OffDeviceMnemonic, + ); + assert!(res.is_ok()); + } + + #[test] + fn thresehold_password_alone_is_err() { + // ARRANGE + let sut = make(); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Password, + ); + assert!(res.is_err()); + } + + #[test] + fn thresehold_password_not_alone() { + // ARRANGE + let mut sut = make(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_arculus(), + ) + .unwrap(); + _ = sut.set_threshold(2); + + // ASSERT + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Password, + ); + assert!(res.is_ok()); + } + + #[test] + fn device_is_err_for_second_3x_threshold() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_2x_threshold_override() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_threshold_override_threshold() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_threshold_override_2x() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_3x_override() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_2x_override_threshold() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_override_threshold_2x() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + + #[test] + fn device_is_err_for_second_override_threshold_override() { + let mut sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_ok()); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + FactorSourceKind::Device, + ); + assert!(res.is_err()); + } + } +} + +mod shield_configs { + use super::*; + + mod mvp { + + use super::*; + + #[test] + fn config_11() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + ) + ); + assert_eq!(built, MatrixOfFactorSourceIds::sample_config_11()); + } + + #[test] + fn config_12() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + let res = sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_password(), + ); + + assert_eq!( + res, + Err(MatrixBuilderValidation::RoleInIsolation { role: RoleKind::Primary, violation: RoleBuilderValidation::NotYetValid(NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne)} + )); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_password() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + ) + ); + } + + #[test] + fn config_13() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + let res = sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_password(), + ); + + assert_eq!( + res, + Err(MatrixBuilderValidation::RoleInIsolation { role: RoleKind::Primary, violation: RoleBuilderValidation::NotYetValid(NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne)} + )); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_device(), + FactorSourceID::sample_password() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + ) + ); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_13() + ) + } + + #[test] + fn config_14() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 1, + [FactorSourceID::sample_device(),], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger() + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_14() + ) + } + + #[test] + fn config_15() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 1, + [FactorSourceID::sample_ledger(),], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_device() + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_15() + ) + } + + #[test] + fn config_21() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_device() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_21() + ) + } + + #[test] + fn config_22() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_device() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_22() + ) + } + + #[test] + fn config_23() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 1, + [FactorSourceID::sample_ledger(),], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger_other(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_device() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_23() + ) + } + + #[test] + fn config_24() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.set_threshold(1).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 1, + [FactorSourceID::sample_device(),], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_ledger_other() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_24() + ) + } + + #[test] + fn config_30() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_device(), + FactorSourceID::sample_password() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_30() + ) + } + + #[test] + fn config_40() { + let mut sut = make(); + + // Primary + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + sut.set_threshold(2).unwrap(); + + // Recovery + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_device(), + ) + .unwrap(); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + + // Confirmation + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password(), + ) + .unwrap(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_password_other(), + ) + .unwrap(); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_off_device(), + ) + .unwrap(); + + // Build + assert!(sut.validate().is_ok()); + let built = sut.build().unwrap(); + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::with_roles( + RoleWithFactorSourceIds::primary_with_factors( + 2, + [ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ], + [], + ), + RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger(), + ],), + RoleWithFactorSourceIds::confirmation_with_factors([ + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_off_device() + ],), + ) + ); + + pretty_assertions::assert_eq!( + built, + MatrixOfFactorSourceIds::sample_config_40() + ) + } + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs new file mode 100644 index 000000000..21d6cfcfb --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -0,0 +1,488 @@ +use crate::prelude::*; + +/// A Matrix of FactorSourceTemplates, can be used to create template +/// "SecurityShields", mostly useful for coding/tests, but theoretically +/// we could UniFFI export these and use them in the hosts wallets, which would +/// pre-populate SecurityShield-builder flow screens - if hosts/Sargon manages +/// to assign each template "slot" with a concrete FactorSourceID, known as +/// materialization. +pub type MatrixTemplate = AbstractMatrixBuilt; + +impl AbstractBuiltRoleWithFactor { + /// Tries to materialize a RoleWithFactorSourceIds from a RoleTemplate by + /// assigning each template with a concrete FactorSourceID using the FactorSourceIdAssigner. + pub(crate) fn assign( + self, + factor_source_id_assigner: &mut FactorSourceIdAssigner, + ) -> Result, CommonError> { + let mut fulfill = + |xs: &Vec| -> Result, CommonError> { + xs.iter() + .map(|f| factor_source_id_assigner.next(f)) + .collect::, CommonError>>() + }; + Ok(RoleWithFactorSourceIds::with_factors( + self.get_threshold(), + fulfill(self.get_threshold_factors())?, + fulfill(self.get_override_factors())?, + )) + } +} + +/// A helper which assigns FactorSourceIDs to FactorSourceTemplates, used for +/// materializing a MatrixTemplate into a MatrixOfFactorSourceIds. +pub(crate) struct FactorSourceIdAssigner { + factor_source_ids: Vec, + map: IndexMap, +} + +impl FactorSourceIdAssigner { + fn new( + factor_source_ids: impl IntoIterator, + ) -> Self { + Self { + factor_source_ids: factor_source_ids.into_iter().collect_vec(), + map: IndexMap::new(), + } + } + + fn next( + &mut self, + template: &FactorSourceTemplate, + ) -> Result { + if let Some(existing) = self.map.get(template) { + Ok(*existing) + } else if let Some(index_of_next) = self + .factor_source_ids + .iter() + .position(|f| f.get_factor_source_kind() == template.kind) + { + let next = self.factor_source_ids.remove(index_of_next); + self.map.insert(template.clone(), next); + Ok(next) + } else { + Err(CommonError::Unknown) + } + } +} + +impl MatrixTemplate { + /// Tries to materialize a MatrixOfFactorSourceIds from a MatrixTemplate by + /// assigning each template with a concrete FactorSourceID using the `factor_source_ids`.` + pub fn materialize( + self, + factor_source_ids: impl IntoIterator, + ) -> Result { + let number_of_days_until_auto_confirm = + self.number_of_days_until_auto_confirm; + let mut assigner = FactorSourceIdAssigner::new(factor_source_ids); + let primary_role = self.primary_role.assign(&mut assigner)?; + let recovery_role = self.recovery_role.assign(&mut assigner)?; + let confirmation_role = self.confirmation_role.assign(&mut assigner)?; + + Ok(MatrixOfFactorSourceIds { + built: PhantomData, + primary_role, + recovery_role, + confirmation_role, + number_of_days_until_auto_confirm, + }) + } +} + +impl MatrixTemplate { + fn new( + primary_role: PrimaryRoleTemplate, + recovery_role: RecoveryRoleTemplate, + confirmation_role: ConfirmationRoleTemplate, + ) -> Self { + Self { + built: PhantomData, + primary_role, + recovery_role, + confirmation_role, + number_of_days_until_auto_confirm: + MatrixOfFactorSourceIds::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + } + } + + pub fn config_11() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::password()]), + ) + } + + pub fn config_12() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::ledger(), + FactorSourceTemplate::password(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::password()]), + ) + } + + pub fn config_13() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::password(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::password()]), + ) + } + + pub fn config_14() -> Self { + Self::new( + PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), + RecoveryRoleTemplate::new([FactorSourceTemplate::ledger()]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::password()]), + ) + } + + pub fn config_15() -> Self { + Self::new( + PrimaryRoleTemplate::new([FactorSourceTemplate::ledger()]), + RecoveryRoleTemplate::new([FactorSourceTemplate::device()]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::password()]), + ) + } + + pub fn config_21() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::ledger(), + FactorSourceTemplate::ledger_other(), + ]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::device()]), + ) + } + + pub fn config_22() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::ledger(), + FactorSourceTemplate::ledger_other(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::ledger(), + FactorSourceTemplate::ledger_other(), + ]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::device()]), + ) + } + + pub fn config_23() -> Self { + Self::new( + PrimaryRoleTemplate::new([FactorSourceTemplate::ledger()]), + RecoveryRoleTemplate::new([FactorSourceTemplate::ledger_other()]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::device()]), + ) + } + + pub fn config_24() -> Self { + Self::new( + PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), + RecoveryRoleTemplate::new([FactorSourceTemplate::ledger()]), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::ledger_other(), + ]), + ) + } + + pub fn config_30() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::ledger(), + FactorSourceTemplate::ledger_other(), + ]), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::password(), + ]), + ) + } + + pub fn config_40() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::password(), + FactorSourceTemplate::password_other(), + FactorSourceTemplate::off_device_mnemonic(), + ]), + ) + } + + pub fn config_51() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::password(), + ]), + RecoveryRoleTemplate::new( + [FactorSourceTemplate::trusted_contact()], + ), + ConfirmationRoleTemplate::new([FactorSourceTemplate::password()]), + ) + } + + pub fn config_52() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::password(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::trusted_contact(), + FactorSourceTemplate::trusted_contact_other(), + FactorSourceTemplate::device(), + ]), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::password(), + FactorSourceTemplate::password_other(), + FactorSourceTemplate::off_device_mnemonic(), + ]), + ) + } + + pub fn config_60() -> Self { + Self::new( + PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), + RecoveryRoleTemplate::new( + [FactorSourceTemplate::trusted_contact()], + ), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::security_questions(), + ]), + ) + } + + pub fn config_70() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::trusted_contact(), + FactorSourceTemplate::ledger(), + ]), + ConfirmationRoleTemplate::new([FactorSourceTemplate::device()]), + ) + } + + pub fn config_80() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::ledger(), + FactorSourceTemplate::device(), + ]), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::security_questions(), + ]), + ) + } + + pub fn config_90() -> Self { + Self::new( + PrimaryRoleTemplate::new([ + FactorSourceTemplate::device(), + FactorSourceTemplate::ledger(), + ]), + RecoveryRoleTemplate::new([ + FactorSourceTemplate::trusted_contact(), + FactorSourceTemplate::device(), + ]), + ConfirmationRoleTemplate::new([ + FactorSourceTemplate::security_questions(), + ]), + ) + } +} + +#[cfg(test)] +mod test_templates { + use super::*; + + fn test_template( + template: MatrixTemplate, + expected: MatrixOfFactorSourceIds, + ) { + let m = template + .materialize(*ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD) + .unwrap(); + pretty_assertions::assert_eq!(m, expected); + } + + #[test] + fn template_config_11() { + test_template( + MatrixTemplate::config_11(), + MatrixOfFactorSourceIds::sample_config_11(), + ) + } + + #[test] + fn template_config_12() { + test_template( + MatrixTemplate::config_12(), + MatrixOfFactorSourceIds::sample_config_12(), + ) + } + + #[test] + fn template_config_13() { + test_template( + MatrixTemplate::config_13(), + MatrixOfFactorSourceIds::sample_config_13(), + ) + } + + #[test] + fn template_config_14() { + test_template( + MatrixTemplate::config_14(), + MatrixOfFactorSourceIds::sample_config_14(), + ) + } + + #[test] + fn template_config_15() { + test_template( + MatrixTemplate::config_15(), + MatrixOfFactorSourceIds::sample_config_15(), + ) + } + + #[test] + fn template_config_21() { + test_template( + MatrixTemplate::config_21(), + MatrixOfFactorSourceIds::sample_config_21(), + ) + } + + #[test] + fn template_config_22() { + test_template( + MatrixTemplate::config_22(), + MatrixOfFactorSourceIds::sample_config_22(), + ) + } + + #[test] + fn template_config_23() { + test_template( + MatrixTemplate::config_23(), + MatrixOfFactorSourceIds::sample_config_23(), + ) + } + + #[test] + fn template_config_24() { + test_template( + MatrixTemplate::config_24(), + MatrixOfFactorSourceIds::sample_config_24(), + ) + } + + #[test] + fn template_config_30() { + test_template( + MatrixTemplate::config_30(), + MatrixOfFactorSourceIds::sample_config_30(), + ) + } + + #[test] + fn template_config_40() { + test_template( + MatrixTemplate::config_40(), + MatrixOfFactorSourceIds::sample_config_40(), + ) + } + + #[test] + fn template_config_51() { + test_template( + MatrixTemplate::config_51(), + MatrixOfFactorSourceIds::sample_config_51(), + ) + } + + #[test] + fn template_config_52() { + test_template( + MatrixTemplate::config_52(), + MatrixOfFactorSourceIds::sample_config_52(), + ) + } + + #[test] + fn template_config_60() { + test_template( + MatrixTemplate::config_60(), + MatrixOfFactorSourceIds::sample_config_60(), + ) + } + + #[test] + fn template_config_70() { + test_template( + MatrixTemplate::config_70(), + MatrixOfFactorSourceIds::sample_config_70(), + ) + } + + #[test] + fn template_config_80() { + test_template( + MatrixTemplate::config_80(), + MatrixOfFactorSourceIds::sample_config_80(), + ) + } + + #[test] + fn template_config_90() { + test_template( + MatrixTemplate::config_90(), + MatrixOfFactorSourceIds::sample_config_90(), + ) + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs new file mode 100644 index 000000000..4e87cc622 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs @@ -0,0 +1,9 @@ +mod error; +mod matrix_builder; +mod matrix_builder_unit_tests; +mod matrix_template; + +pub use error::*; +#[allow(unused_imports)] +pub use matrix_builder::*; +pub use matrix_template::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs new file mode 100644 index 000000000..1eb0cff8d --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs @@ -0,0 +1,72 @@ +use crate::prelude::*; + +impl FactorSourceID { + pub(crate) fn sample_device() -> Self { + FactorSourceIDFromHash::sample_device().into() + } + pub(crate) fn sample_ledger() -> Self { + FactorSourceIDFromHash::sample_ledger().into() + } + pub(crate) fn sample_ledger_other() -> Self { + FactorSourceIDFromHash::sample_ledger_other().into() + } + pub(crate) fn sample_arculus() -> Self { + FactorSourceIDFromHash::sample_arculus().into() + } + pub(crate) fn sample_arculus_other() -> Self { + FactorSourceIDFromHash::sample_arculus_other().into() + } + + pub(crate) fn sample_password() -> Self { + FactorSourceIDFromHash::sample_password().into() + } + + pub(crate) fn sample_password_other() -> Self { + FactorSourceIDFromHash::sample_password_other().into() + } + + /// Radix Wallet (UI) calls this "passphrase" + pub(crate) fn sample_off_device() -> Self { + FactorSourceIDFromHash::sample_off_device().into() + } + /// Radix Wallet (UI) calls this "passphrase" + pub(crate) fn sample_off_device_other() -> Self { + FactorSourceIDFromHash::sample_off_device_other().into() + } + pub(crate) fn sample_security_questions() -> Self { + FactorSourceIDFromHash::sample_security_questions().into() + } + pub(crate) fn sample_device_other() -> Self { + FactorSourceIDFromHash::sample_device_other().into() + } + pub(crate) fn sample_security_questions_other() -> Self { + FactorSourceIDFromHash::sample_security_questions_other().into() + } + pub(crate) fn sample_trusted_contact() -> Self { + FactorSource::sample_trusted_contact_frank().id() + } + pub(crate) fn sample_trusted_contact_other() -> Self { + FactorSource::sample_trusted_contact_grace().id() + } +} + +#[allow(dead_code)] +pub static ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD: Lazy<[FactorSourceID; 14]> = + Lazy::new(|| { + [ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other(), + FactorSourceID::sample_arculus(), + FactorSourceID::sample_arculus_other(), + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_off_device(), + FactorSourceID::sample_off_device_other(), + FactorSourceID::sample_security_questions(), + FactorSourceID::sample_device_other(), + FactorSourceID::sample_security_questions_other(), + FactorSourceID::sample_trusted_contact(), + FactorSourceID::sample_trusted_contact_other(), + ] + }); diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs new file mode 100644 index 000000000..6eef809af --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs @@ -0,0 +1,409 @@ +use crate::prelude::*; + +pub type MatrixOfFactorInstances = + AbstractMatrixBuilderOrBuilt; + +pub trait HasFactorInstances { + fn unique_factor_instances(&self) -> IndexSet; +} + +pub trait HasFactorSourceKindObjectSafe { + fn get_factor_source_kind(&self) -> FactorSourceKind; +} +impl HasFactorSourceKindObjectSafe for FactorSourceID { + fn get_factor_source_kind(&self) -> FactorSourceKind { + match self { + FactorSourceID::Hash { value } => value.kind, + FactorSourceID::Address { value } => value.kind, + } + } +} + +impl HasFactorInstances for MatrixOfFactorInstances { + fn unique_factor_instances(&self) -> IndexSet { + let mut set = IndexSet::new(); + set.extend(self.primary_role.all_factors().into_iter().cloned()); + set.extend(self.recovery_role.all_factors().into_iter().cloned()); + set.extend(self.confirmation_role.all_factors().into_iter().cloned()); + set + } +} + +impl MnemonicWithPassphrase { + fn derive_instances_for_factor_sources( + network_id: NetworkID, + quantity_per_factor: usize, + derivation_presets: impl IntoIterator, + sources: impl IntoIterator, + ) -> IndexMap { + // let next_index_assigner = NextDerivationEntityIndexAssigner::new( + // network_id, + // None, + // FactorInstancesCache::default(), + // ); + + // let derivation_presets = + // derivation_presets.into_iter().collect::>(); + + // sources + // .into_iter() + // .map(|fs| { + // let fsid = fs.id_from_hash(); + // let mwp = fsid.sample_associated_mnemonic(); + + // let paths = derivation_presets + // .clone() + // .into_iter() + // .map(|dp| (dp, quantity_per_factor)) + // .collect::>(); + + // let paths = paths + // .into_iter() + // .flat_map(|(derivation_preset, qty)| { + // // `qty` many paths + // (0..qty) + // .map(|_| { + // let index_agnostic_path = derivation_preset + // .index_agnostic_path_on_network(network_id); + + // next_index_assigner + // .next(fsid, index_agnostic_path) + // .map(|index| { + // DerivationPath::from(( + // index_agnostic_path, + // index, + // )) + // }) + // .unwrap() + // }) + // .collect::>() + // }) + // .collect::>(); + + // let instances = mwp + // .derive_public_keys(paths) + // .into_iter() + // .map(|public_key| { + // HierarchicalDeterministicFactorInstance::new( + // fsid, public_key, + // ) + // }) + // .collect::(); + + // (fsid, instances) + // }) + // .collect::>() + + todo!("use FIP") + } +} + +impl MatrixOfFactorInstances { + fn sample_from_matrix_of_sources( + matrix_of_sources: MatrixOfFactorSources, + ) -> Self { + let mut consuming_instances = + MnemonicWithPassphrase::derive_instances_for_factor_sources( + NetworkID::Mainnet, + 1, + [DerivationPreset::AccountMfa], + matrix_of_sources.all_factors().into_iter().cloned(), + ); + + Self::fulfilling_matrix_of_factor_sources_with_instances( + &mut consuming_instances, + matrix_of_sources.clone(), + ) + .unwrap() + } +} + +impl HasSampleValues for MatrixOfFactorInstances { + fn sample() -> Self { + Self::sample_from_matrix_of_sources(MatrixOfFactorSources::sample()) + } + + fn sample_other() -> Self { + Self::sample_from_matrix_of_sources( + MatrixOfFactorSources::sample_other(), + ) + } +} + +impl MatrixOfFactorInstances { + /// Maps `MatrixOfFactorSources -> MatrixOfFactorInstances` by + /// "assigning" FactorInstances to each MatrixOfFactorInstances from + /// `consuming_instances`. + /// + /// NOTE: + /// **One FactorInstance might be used multiple times in the MatrixOfFactorInstances, + /// e.g. ones in the PrimaryRole(WithFactorInstances) and again in RecoveryRole(WithFactorInstances) or + /// in RecoveryRole(WithFactorInstances)**. + /// + /// However, the same FactorInstance is NEVER used in two different MatrixOfFactorInstances. + /// + /// + pub fn fulfilling_matrix_of_factor_sources_with_instances( + consuming_instances: &mut IndexMap< + FactorSourceIDFromHash, + FactorInstances, + >, + matrix_of_factor_sources: MatrixOfFactorSources, + ) -> Result { + let instances = &consuming_instances.clone(); + + let primary_role = + PrimaryRoleWithFactorInstances::fulfilling_role_of_factor_sources_with_factor_instances( + instances, + &matrix_of_factor_sources, + )?; + let recovery_role = + RecoveryRoleWithFactorInstances::fulfilling_role_of_factor_sources_with_factor_instances( + instances, + &matrix_of_factor_sources, + )?; + let confirmation_role = + ConfirmationRoleWithFactorInstances::fulfilling_role_of_factor_sources_with_factor_instances( + instances, + &matrix_of_factor_sources, + )?; + + let matrix = Self { + built: PhantomData, + primary_role, + recovery_role, + confirmation_role, + number_of_days_until_auto_confirm: matrix_of_factor_sources + .number_of_days_until_auto_confirm, + }; + + // Now that we have assigned instances, **possibly the SAME INSTANCE to multiple roles**, + // lets delete them from the `consuming_instances` map. + for instance in matrix.all_factors() { + let fsid = + &FactorSourceIDFromHash::try_from(instance.factor_source_id) + .unwrap(); + let existing = consuming_instances.get_mut(fsid).unwrap(); + + let to_remove = HierarchicalDeterministicFactorInstance::try_from( + instance.clone(), + ) + .unwrap(); + + // We remove at the beginning of the list first. + existing.shift_remove(&to_remove); + + if existing.is_empty() { + // not needed per se, but feels prudent to "prune". + consuming_instances.shift_remove_entry(fsid); + } + } + + Ok(matrix) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = MatrixOfFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + assert_ne!( + SUT::sample().unique_factor_instances(), + SUT::sample_other().unique_factor_instances() + ); + } + + #[test] + fn err_if_no_instance_found_for_factor_source() { + assert!(matches!( + SUT::fulfilling_matrix_of_factor_sources_with_instances( + &mut IndexMap::new(), + MatrixOfFactorSources::sample() + ), + Err(CommonError::MissingFactorMappingInstancesIntoRole) + )); + } + + #[test] + fn err_if_empty_instance_found_for_factor_source() { + assert!(matches!( + SUT::fulfilling_matrix_of_factor_sources_with_instances( + &mut IndexMap::kv( + FactorSource::sample_device_babylon().id_from_hash(), + FactorInstances::from_iter([]) + ), + MatrixOfFactorSources::sample() + ), + Err(CommonError::MissingFactorMappingInstancesIntoRole) + )); + } + + #[test] + fn assert_json_sample() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "primaryRole": { + "threshold": 2, + "thresholdFactors": [ + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "427969814e15d74c3ff4d9971465cb709d210c8a7627af9466bdaa67bd0929b7" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + }, + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + } + ], + "overrideFactors": [] + }, + "recoveryRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "427969814e15d74c3ff4d9971465cb709d210c8a7627af9466bdaa67bd0929b7" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + }, + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + } + ] + }, + "confirmationRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "password", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "4af49eb56b1af579aaf03f1760ec526f56e2297651f7a067f4b362f685417a81" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + } + ] + }, + "numberOfDaysUntilAutoConfirm": 14 + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs new file mode 100644 index 000000000..d0c644456 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -0,0 +1,315 @@ +use crate::prelude::*; + +pub type MatrixOfFactorSourceIds = AbstractMatrixBuilt; + +#[cfg(test)] +impl MatrixOfFactorSourceIds { + pub(crate) fn with_roles_and_days( + primary: PrimaryRoleWithFactorSourceIds, + recovery: RecoveryRoleWithFactorSourceIds, + confirmation: ConfirmationRoleWithFactorSourceIds, + number_of_days_until_auto_confirm: u16, + ) -> Self { + assert_eq!(primary.role(), RoleKind::Primary); + assert_eq!(recovery.role(), RoleKind::Recovery); + assert_eq!(confirmation.role(), RoleKind::Confirmation); + Self { + built: PhantomData, + primary_role: primary, + recovery_role: recovery, + confirmation_role: confirmation, + number_of_days_until_auto_confirm, + } + } + + pub(crate) fn with_roles( + primary: PrimaryRoleWithFactorSourceIds, + recovery: RecoveryRoleWithFactorSourceIds, + confirmation: ConfirmationRoleWithFactorSourceIds, + ) -> Self { + Self::with_roles_and_days( + primary, + recovery, + confirmation, + Self::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + ) + } +} + +impl MatrixOfFactorSourceIds { + fn sample_from_template(template: MatrixTemplate) -> Self { + template + .materialize(*ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD) + .unwrap() + } + + pub fn sample_config_11() -> Self { + Self::sample_from_template(MatrixTemplate::config_11()) + } + + pub fn sample_config_12() -> Self { + Self::sample_from_template(MatrixTemplate::config_12()) + } + + pub fn sample_config_13() -> Self { + Self::sample_from_template(MatrixTemplate::config_13()) + } + + pub fn sample_config_14() -> Self { + Self::sample_from_template(MatrixTemplate::config_14()) + } + + pub fn sample_config_15() -> Self { + Self::sample_from_template(MatrixTemplate::config_15()) + } + + pub fn sample_config_21() -> Self { + Self::sample_from_template(MatrixTemplate::config_21()) + } + + pub fn sample_config_22() -> Self { + Self::sample_from_template(MatrixTemplate::config_22()) + } + + pub fn sample_config_23() -> Self { + Self::sample_from_template(MatrixTemplate::config_23()) + } + + pub fn sample_config_24() -> Self { + Self::sample_from_template(MatrixTemplate::config_24()) + } + + pub fn sample_config_30() -> Self { + Self::sample_from_template(MatrixTemplate::config_30()) + } + + pub fn sample_config_40() -> Self { + Self::sample_from_template(MatrixTemplate::config_40()) + } + + pub fn sample_config_51() -> Self { + Self::sample_from_template(MatrixTemplate::config_51()) + } + + pub fn sample_config_52() -> Self { + Self::sample_from_template(MatrixTemplate::config_52()) + } + + pub fn sample_config_60() -> Self { + Self::sample_from_template(MatrixTemplate::config_60()) + } + + pub fn sample_config_70() -> Self { + Self::sample_from_template(MatrixTemplate::config_70()) + } + + pub fn sample_config_80() -> Self { + Self::sample_from_template(MatrixTemplate::config_80()) + } + + pub fn sample_config_90() -> Self { + Self::sample_from_template(MatrixTemplate::config_90()) + } +} + +impl HasSampleValues for MatrixOfFactorSourceIds { + fn sample() -> Self { + Self::sample_config_11() + } + + fn sample_other() -> Self { + Self::sample_config_24() + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[allow(clippy::upper_case_acronyms)] + type SUT = MatrixOfFactorSourceIds; + + #[test] + fn template() {} + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + assert_ne!(SUT::sample(), SUT::sample_config_12()); + assert_ne!(SUT::sample().primary(), SUT::sample_other().primary()); + assert_ne!(SUT::sample().recovery(), SUT::sample_other().recovery()); + assert_ne!( + SUT::sample().confirmation(), + SUT::sample_other().confirmation() + ); + } + + #[test] + fn hash() { + assert_eq!( + HashSet::::from_iter([ + SUT::sample_config_11(), + SUT::sample_config_12(), + SUT::sample_config_13(), + SUT::sample_config_14(), + SUT::sample_config_15(), + SUT::sample_config_21(), + SUT::sample_config_22(), + SUT::sample_config_23(), + SUT::sample_config_24(), + SUT::sample_config_30(), + SUT::sample_config_40(), + SUT::sample_config_51(), + SUT::sample_config_52(), + SUT::sample_config_60(), + SUT::sample_config_70(), + SUT::sample_config_80(), + SUT::sample_config_90(), + // Duplicates should be removed + SUT::sample_config_11(), + SUT::sample_config_12(), + SUT::sample_config_13(), + SUT::sample_config_14(), + SUT::sample_config_15(), + SUT::sample_config_21(), + SUT::sample_config_22(), + SUT::sample_config_23(), + SUT::sample_config_24(), + SUT::sample_config_30(), + SUT::sample_config_40(), + SUT::sample_config_51(), + SUT::sample_config_52(), + SUT::sample_config_60(), + SUT::sample_config_70(), + SUT::sample_config_80(), + SUT::sample_config_90(), + ]) + .len(), + 17 + ); + } + + #[test] + fn assert_json_sample() { + let sut = SUT::sample(); + + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "primaryRole": { + "threshold": 2, + "thresholdFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ], + "overrideFactors": [] + }, + "recoveryRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ] + }, + "confirmationRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "password", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" + } + } + ] + }, + "numberOfDaysUntilAutoConfirm": 14 + } + "#, + ); + } + + #[test] + fn assert_json_sample_other() { + let sut = SUT::sample_other(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "primaryRole": { + "threshold": 1, + "thresholdFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + } + ], + "overrideFactors": [] + }, + "recoveryRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ] + }, + "confirmationRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "52ef052a0642a94279b296d6b3b17dedc035a7ae37b76c1d60f11f2725100077" + } + } + ] + }, + "numberOfDaysUntilAutoConfirm": 14 + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs new file mode 100644 index 000000000..99bdfb580 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs @@ -0,0 +1,70 @@ +use crate::prelude::*; + +pub type MatrixOfFactorSources = AbstractMatrixBuilt; + +impl MatrixOfFactorSources { + pub fn new( + matrix: MatrixOfFactorSourceIds, + factor_sources: &FactorSources, + ) -> Result { + let primary_role = + RoleWithFactorSources::new(matrix.primary_role, factor_sources)?; + + let recovery_role = + RoleWithFactorSources::new(matrix.recovery_role, factor_sources)?; + + let confirmation_role = RoleWithFactorSources::new( + matrix.confirmation_role, + factor_sources, + )?; + + if primary_role.role() != RoleKind::Primary + || recovery_role.role() != RoleKind::Recovery + || confirmation_role.role() != RoleKind::Confirmation + { + unreachable!("Programmer error!") + } + + Ok(Self { + built: PhantomData, + primary_role, + recovery_role, + confirmation_role, + number_of_days_until_auto_confirm: matrix + .number_of_days_until_auto_confirm, + }) + } +} + +impl HasSampleValues for MatrixOfFactorSources { + fn sample() -> Self { + let ids = MatrixOfFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } + + fn sample_other() -> Self { + let ids = MatrixOfFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = MatrixOfFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs new file mode 100644 index 000000000..39708a983 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs @@ -0,0 +1,14 @@ +mod abstract_matrix_builder_or_built; +mod builder; +mod factor_source_id_samples; +mod matrix_of_factor_instances; +mod matrix_of_factor_source_ids; +mod matrix_of_factor_sources; + +pub(crate) use abstract_matrix_builder_or_built::*; +#[allow(unused_imports)] +pub use builder::*; +pub(crate) use factor_source_id_samples::*; +pub use matrix_of_factor_instances::*; +pub use matrix_of_factor_source_ids::*; +pub use matrix_of_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/mod.rs b/crates/sargon/src/profile/mfa/security_structures/mod.rs index b3941560c..0472c3fe4 100644 --- a/crates/sargon/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/mod.rs @@ -1,15 +1,13 @@ -mod decl_security_structure_of; -mod factor_instance_level; -mod factor_source_id_level; -mod factor_source_level; mod has_role_kind; -mod role_with_factors; +mod matrices; +mod roles; +mod security_structure_id; mod security_structure_metadata; +mod security_structure_of_factors; -pub(crate) use decl_security_structure_of::*; -pub use factor_instance_level::*; -pub use factor_source_id_level::*; -pub use factor_source_level::*; pub use has_role_kind::*; -pub use role_with_factors::*; +pub use matrices::*; +pub use roles::*; +pub use security_structure_id::*; pub use security_structure_metadata::*; +pub use security_structure_of_factors::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/role_with_factors.rs b/crates/sargon/src/profile/mfa/security_structures/role_with_factors.rs deleted file mode 100644 index f7faa1a73..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/role_with_factors.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::prelude::*; - -pub trait RoleWithFactors { - fn get_threshold_factors(&self) -> &Vec; - fn get_threshold(&self) -> u8; - fn get_override_factors(&self) -> &Vec; - - fn all_factors(&self) -> IndexSet<&Factor> { - let mut factors = - IndexSet::from_iter(self.get_threshold_factors().iter()); - factors.extend(self.get_override_factors().iter()); - factors - } -} - -pub trait HasFactorInstances { - fn unique_factor_instances(&self) -> IndexSet; -} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs new file mode 100644 index 000000000..f27db513c --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -0,0 +1,147 @@ +use std::marker::PhantomData; + +use serde::{Deserialize, Serialize}; + +use crate::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AbstractRoleBuilderOrBuilt { + #[serde(skip)] + #[doc(hidden)] + built: PhantomData, + + threshold: u8, + threshold_factors: Vec, + override_factors: Vec, +} + +pub(crate) type AbstractBuiltRoleWithFactor = + AbstractRoleBuilderOrBuilt; +pub(crate) type RoleBuilder = + AbstractRoleBuilderOrBuilt; + +impl + AbstractRoleBuilderOrBuilt +{ + pub fn role(&self) -> RoleKind { + RoleKind::from_u8(R).expect("RoleKind should be valid") + } + + pub(crate) fn with_factors( + threshold: u8, + threshold_factors: impl IntoIterator, + override_factors: impl IntoIterator, + ) -> Self { + let assert_is_securified = + |factors: &Vec| -> Result<(), CommonError> { + let trait_objects: Vec<&dyn IsMaybeKeySpaceAware> = factors + .iter() + .map(|x| x as &dyn IsMaybeKeySpaceAware) + .collect(); + if trait_objects + .iter() + .filter_map(|x| x.maybe_key_space()) + .any(|x| x != KeySpace::Securified) + { + return Err( + crate::CommonError::IndexUnsecurifiedExpectedSecurified, + ); + } + Ok(()) + }; + + let threshold_factors = threshold_factors.into_iter().collect(); + let override_factors = override_factors.into_iter().collect(); + + assert_is_securified(&threshold_factors) + .expect("Should not have allowed building of invalid Role"); + assert_is_securified(&override_factors) + .expect("Should not have allowed building of invalid Role"); + + Self { + built: PhantomData, + threshold, + threshold_factors, + override_factors, + } + } +} + +impl AbstractRoleBuilderOrBuilt { + pub fn all_factors(&self) -> Vec<&F> { + self.threshold_factors + .iter() + .chain(self.override_factors.iter()) + .collect() + } + + pub fn get_threshold_factors(&self) -> &Vec { + &self.threshold_factors + } + + pub fn get_override_factors(&self) -> &Vec { + &self.override_factors + } + + pub fn get_threshold(&self) -> u8 { + self.threshold + } +} +pub(crate) const ROLE_PRIMARY: u8 = 1; +pub(crate) const ROLE_RECOVERY: u8 = 2; +pub(crate) const ROLE_CONFIRMATION: u8 = 3; + +pub(crate) trait RoleFromDiscriminator { + fn from_u8(discriminator: u8) -> Option + where + Self: Sized; +} +impl RoleFromDiscriminator for RoleKind { + fn from_u8(discriminator: u8) -> Option { + match discriminator { + ROLE_PRIMARY => Some(RoleKind::Primary), + ROLE_RECOVERY => Some(RoleKind::Recovery), + ROLE_CONFIRMATION => Some(RoleKind::Confirmation), + _ => None, + } + } +} + +impl RoleBuilder { + pub(crate) fn new() -> Self { + Self { + built: PhantomData, + threshold: 0, + threshold_factors: Vec::new(), + override_factors: Vec::new(), + } + } + + pub(crate) fn mut_threshold_factors(&mut self) -> &mut Vec { + &mut self.threshold_factors + } + + pub(crate) fn mut_override_factors(&mut self) -> &mut Vec { + &mut self.override_factors + } + + pub(crate) fn unchecked_add_factor_source_to_list( + &mut self, + factor_source_id: FactorSourceID, + factor_list_kind: FactorListKind, + ) { + match factor_list_kind { + FactorListKind::Threshold => { + self.threshold_factors.push(factor_source_id) + } + FactorListKind::Override => { + self.override_factors.push(factor_source_id) + } + } + } + + pub(crate) fn unchecked_set_threshold(&mut self, threshold: u8) { + self.threshold = threshold; + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs new file mode 100644 index 000000000..82c8bf196 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs @@ -0,0 +1,350 @@ +#![cfg(test)] + +use crate::prelude::*; + +#[allow(clippy::upper_case_acronyms)] + +type MutRes = RoleBuilderMutateResult; + +#[test] +fn new_builder_confirmation() { + assert_eq!( + ConfirmationRoleBuilder::new().role(), + RoleKind::Confirmation + ); +} + +#[test] +fn empty_is_err_confirmation() { + let sut = ConfirmationRoleBuilder::new(); + let res = sut.build(); + assert_eq!( + res, + Result::not_yet_valid(NotYetValidReason::RoleMustHaveAtLeastOneFactor) + ); +} + +#[allow(clippy::upper_case_acronyms)] +type SUT = ConfirmationRoleBuilder; + +fn make() -> SUT { + SUT::new() +} + +#[test] +fn validation_for_addition_of_factor_source_of_kind_to_list() { + use FactorSourceKind::*; + let sut = make(); + let not_ok = |kind: FactorSourceKind| { + let res = sut + .validation_for_addition_of_factor_source_of_kind_to_override(kind); + assert!(res.is_err()); + }; + let ok = |kind: FactorSourceKind| { + let res = sut + .validation_for_addition_of_factor_source_of_kind_to_override(kind); + assert!(res.is_ok()); + }; + ok(Device); + ok(LedgerHQHardwareWallet); + ok(ArculusCard); + ok(SecurityQuestions); + ok(Password); + ok(OffDeviceMnemonic); + not_ok(TrustedContact); +} + +mod device_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_device() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_device_other() + } + + #[test] + fn set_threshold_is_unsupported() { + let mut sut = make(); + assert_eq!( + sut.set_threshold(1), + MutRes::basic_violation( + BasicViolation::ConfirmationCannotSetThreshold + ) + ); + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([sample()]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + let built = sut.build().unwrap(); + assert!(built.get_threshold_factors().is_empty()); + assert_eq!( + built, + RoleWithFactorSourceIds::confirmation_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod ledger_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_ledger() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_ledger_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([sample(),]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod arculus_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_arculus() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_arculus_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([sample(),]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod off_device_mnemonic_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_off_device() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_off_device_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([sample(),]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod trusted_contact_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_trusted_contact() + } + + #[test] + fn unsupported() { + // Arrange + let mut sut = make(); + + // Act + let res = sut.add_factor_source(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::ConfirmationRoleTrustedContactNotSupported + ) + ); + } + + #[test] + fn valid_then_invalid_because_unsupported() { + // Arrange + let mut sut = make(); + + sut.add_factor_source(FactorSourceID::sample_ledger()) + .unwrap(); + sut.add_factor_source(FactorSourceID::sample_arculus()) + .unwrap(); + + // Act + let res = sut.add_factor_source(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::ConfirmationRoleTrustedContactNotSupported + ) + ); + } +} + +mod password_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_password() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_password_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([sample(),]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::confirmation_with_factors([ + sample(), + sample_other() + ]) + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs new file mode 100644 index 000000000..af9dc07f3 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs @@ -0,0 +1,7 @@ +mod confirmation_roles_builder_unit_tests; +mod primary_roles_builder_unit_tests; +mod recovery_roles_builder_unit_tests; +mod roles_builder; +mod roles_builder_unit_tests; + +pub use roles_builder::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs new file mode 100644 index 000000000..2b9438a0e --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs @@ -0,0 +1,952 @@ +#![cfg(test)] + +use crate::prelude::*; + +use NotYetValidReason::*; +type Validation = RoleBuilderValidation; + +#[allow(clippy::upper_case_acronyms)] + +type MutRes = RoleBuilderMutateResult; + +#[test] +fn new_builder_primary() { + assert_eq!(PrimaryRoleBuilder::new().role(), RoleKind::Primary); +} + +#[test] +fn empty_is_err_primary() { + let sut = PrimaryRoleBuilder::new(); + let res = sut.build(); + assert_eq!( + res, + Result::not_yet_valid(NotYetValidReason::RoleMustHaveAtLeastOneFactor) + ); +} + +mod primary_test_helper_functions { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleBuilder; + + #[test] + fn factor_sources_not_of_kind_to_list_of_kind_in_override() { + let mut sut = SUT::new(); + sut.add_factor_source_to_override(FactorSourceID::sample_device()) + .unwrap(); + sut.add_factor_source_to_override(FactorSourceID::sample_ledger()) + .unwrap(); + sut.add_factor_source_to_override(FactorSourceID::sample_arculus()) + .unwrap(); + + let xs = sut.factor_sources_not_of_kind_to_list_of_kind( + FactorSourceKind::Device, + FactorListKind::Override, + ); + assert_eq!( + xs, + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_arculus() + ] + ); + + let xs = sut.factor_sources_not_of_kind_to_list_of_kind( + FactorSourceKind::LedgerHQHardwareWallet, + FactorListKind::Override, + ); + assert_eq!( + xs, + vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_arculus() + ] + ); + + let xs = sut.factor_sources_not_of_kind_to_list_of_kind( + FactorSourceKind::ArculusCard, + FactorListKind::Override, + ); + assert_eq!( + xs, + vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ] + ); + } + + #[test] + fn factor_sources_not_of_kind_to_list_of_kind_in_threshold() { + let mut sut = SUT::new(); + sut.add_factor_source_to_threshold(FactorSourceID::sample_device()) + .unwrap(); + sut.add_factor_source_to_threshold(FactorSourceID::sample_ledger()) + .unwrap(); + sut.add_factor_source_to_threshold(FactorSourceID::sample_arculus()) + .unwrap(); + + let xs = sut.factor_sources_not_of_kind_to_list_of_kind( + FactorSourceKind::Device, + FactorListKind::Threshold, + ); + assert_eq!( + xs, + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_arculus() + ] + ); + + let xs = sut.factor_sources_not_of_kind_to_list_of_kind( + FactorSourceKind::LedgerHQHardwareWallet, + FactorListKind::Threshold, + ); + assert_eq!( + xs, + vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_arculus() + ] + ); + + let xs = sut.factor_sources_not_of_kind_to_list_of_kind( + FactorSourceKind::ArculusCard, + FactorListKind::Threshold, + ); + assert_eq!( + xs, + vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_ledger() + ] + ); + } +} + +#[allow(clippy::upper_case_acronyms)] +type SUT = PrimaryRoleBuilder; + +fn make() -> SUT { + SUT::new() +} + +#[cfg(test)] +mod threshold_suite { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_device() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_ledger() + } + + fn sample_third() -> FactorSourceID { + FactorSourceID::sample_arculus() + } + + #[test] + fn remove_lowers_threshold_from_1_to_0() { + let mut sut = make(); + let fs = sample(); + sut.add_factor_source_to_threshold(fs).unwrap(); + sut.set_threshold(1).unwrap(); + assert_eq!(sut.get_threshold(), 1); + assert_eq!( + sut.remove_factor_source(&fs), + Err(Validation::NotYetValid(RoleMustHaveAtLeastOneFactor)) + ); + assert_eq!(sut.get_threshold(), 0); + } + + #[test] + fn remove_lowers_threshold_from_3_to_1() { + let mut sut = make(); + let fs0 = sample(); + let fs1 = sample_other(); + sut.add_factor_source_to_threshold(fs0).unwrap(); + sut.add_factor_source_to_threshold(fs1).unwrap(); + sut.add_factor_source_to_threshold( + FactorSourceID::sample_arculus_other(), + ) + .unwrap(); + sut.set_threshold(3).unwrap(); + assert_eq!(sut.get_threshold(), 3); + sut.remove_factor_source(&fs0).unwrap(); + sut.remove_factor_source(&fs1).unwrap(); + assert_eq!(sut.get_threshold(), 1); + } + + #[test] + fn remove_from_override_does_not_change_threshold() { + let mut sut = make(); + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + let fs = FactorSourceID::sample_arculus_other(); + sut.add_factor_source_to_override(fs).unwrap(); + sut.set_threshold(2).unwrap(); + assert_eq!(sut.get_threshold(), 2); + sut.remove_factor_source(&fs).unwrap(); + assert_eq!(sut.get_threshold(), 2); + + let built = sut.build().unwrap(); + assert_eq!(built.get_threshold(), 2); + + assert_eq!(built.role(), RoleKind::Primary); + + assert_eq!( + built.get_threshold_factors(), + &vec![sample(), sample_other()] + ); + + assert_eq!(built.get_override_factors(), &Vec::new()); + } + + #[test] + fn one_factor_then_set_threshold_to_one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + sut.set_threshold(1).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn zero_factor_then_set_threshold_to_one_is_not_yet_valid_then_add_one_factor_is_ok( + ) { + // Arrange + let mut sut = make(); + + // Act + assert_eq!( + sut.set_threshold(1), + Err(Validation::NotYetValid( + ThresholdHigherThanThresholdFactorsLen + )) + ); + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn zero_factor_then_set_threshold_to_two_is_not_yet_valid_then_add_two_factor_is_ok( + ) { + // Arrange + let mut sut = make(); + + // Act + assert_eq!( + sut.set_threshold(2), + Err(Validation::NotYetValid( + ThresholdHigherThanThresholdFactorsLen + )) + ); + sut.add_factor_source_to_threshold(sample()).unwrap(); + + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 2, + [sample(), sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn add_two_factors_then_set_threshold_to_two_is_ok() { + // Arrange + let mut sut = make(); + + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + + // Act + assert_eq!(sut.set_threshold(2), Ok(())); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 2, + [sample(), sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn add_two_factors_then_set_threshold_to_three_is_not_yet_valid_then_add_third_factor_is_ok( + ) { + // Arrange + let mut sut = make(); + + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + + // Act + assert_eq!( + sut.set_threshold(3), + Err(Validation::NotYetValid( + ThresholdHigherThanThresholdFactorsLen + )) + ); + + sut.add_factor_source_to_threshold(sample_third()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 3, + [sample(), sample_other(), sample_third()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn one_factors_set_threshold_of_one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + sut.set_threshold(1).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn one_override_factors_set_threshold_to_one_is_not_yet_valid() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_override(sample_other()).unwrap(); + assert_eq!( + sut.set_threshold(1), + Err(Validation::NotYetValid( + ThresholdHigherThanThresholdFactorsLen + )) + ); + + // Assert + + assert_eq!( + sut.build(), + Err(Validation::NotYetValid( + ThresholdHigherThanThresholdFactorsLen + )) + ); + } + + #[test] + fn validation_for_addition_of_factor_source_for_each_before_after_adding_a_factor( + ) { + let mut sut = make(); + let fs0 = FactorSourceID::sample_ledger(); + let fs1 = FactorSourceID::sample_password(); + let fs2 = FactorSourceID::sample_arculus(); + let xs = sut.validation_for_addition_of_factor_source_for_each( + FactorListKind::Threshold, + &IndexSet::from_iter([fs0, fs1, fs2]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::ok(RoleKind::Primary, fs0,), + FactorSourceInRoleBuilderValidationStatus::not_yet_valid( + RoleKind::Primary, + fs1, + NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor + ), + FactorSourceInRoleBuilderValidationStatus::ok(RoleKind::Primary, fs2,), + ] + ); + _ = sut.add_factor_source_to_threshold(fs0); + _ = sut.set_threshold(2); + + let xs = sut.validation_for_addition_of_factor_source_for_each( + FactorListKind::Threshold, + &IndexSet::from_iter([fs0, fs1, fs2]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Primary, + fs0, + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Primary, + fs1, + ), + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Primary, + fs2, + ), + ] + ); + } +} + +#[cfg(test)] +mod password { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_password() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_password_other() + } + + #[test] + fn test_suite_prerequisite() { + assert_eq!(sample(), sample()); + assert_eq!(sample_other(), sample_other()); + assert_ne!(sample(), sample_other()); + } + + mod threshold_in_isolation { + use super::*; + + #[test] + fn duplicates_not_allowed() { + let mut sut = make(); + sut.add_factor_source_to_threshold(FactorSourceID::sample_device()) + .unwrap(); + _ = sut.set_threshold(2); + test_duplicates_not_allowed( + sut, + FactorListKind::Threshold, + sample(), + ); + } + + #[test] + fn alone_is_not_ok() { + // Arrange + let mut sut = make(); + + // Act + let res = sut.add_factor_source_to_threshold(sample()); + + // Assert + assert_eq!( + res, + MutRes::not_yet_valid( + NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor + ) + ); + } + + #[test] + fn validation_for_addition_of_factor_source_of_kind_to_list() { + use FactorSourceKind::*; + + let not_ok = |kind: FactorSourceKind| { + let sut = make(); + let res = sut + .validation_for_addition_of_factor_source_of_kind_to_list( + kind, + FactorListKind::Threshold, + ); + assert!(res.is_err()); + }; + + let ok_with = |kind: FactorSourceKind, setup: fn(&mut SUT)| { + let mut sut = make(); + setup(&mut sut); + let res = sut + .validation_for_addition_of_factor_source_of_kind_to_list( + kind, + FactorListKind::Threshold, + ); + assert!(res.is_ok()); + }; + let ok = |kind: FactorSourceKind| { + ok_with(kind, |_| {}); + }; + + ok(LedgerHQHardwareWallet); + ok(ArculusCard); + ok(OffDeviceMnemonic); + + ok_with(Device, |sut| { + sut.add_factor_source_to_threshold( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + }); + ok_with(Password, |sut| { + sut.add_factor_source_to_threshold( + FactorSourceID::sample_device(), + ) + .unwrap(); + _ = sut.set_threshold(2); + }); + + not_ok(SecurityQuestions); + not_ok(TrustedContact); + } + } + + mod override_in_isolation { + use super::*; + + #[test] + fn unsupported() { + // Arrange + let mut sut = make(); + + // Act + let res = sut.add_factor_source_to_override(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::PrimaryCannotHavePasswordInOverrideList + ) + ); + } + + #[test] + fn valid_then_invalid_because_unsupported() { + // Arrange + let mut sut = make(); + sut.add_factor_source_to_override(FactorSourceID::sample_device()) + .unwrap(); + sut.add_factor_source_to_override(FactorSourceID::sample_ledger()) + .unwrap(); + sut.add_factor_source_to_override(FactorSourceID::sample_arculus()) + .unwrap(); + + // Act + let res = sut.add_factor_source_to_override(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::PrimaryCannotHavePasswordInOverrideList + ) + ); + } + + #[test] + fn validation_for_addition_of_factor_source_of_kind_to_override() { + use FactorSourceKind::*; + + let not_ok = |kind: FactorSourceKind| { + let sut = make(); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_override(kind); + assert!(res.is_err()); + }; + + let ok_with = |kind: FactorSourceKind, setup: fn(&mut SUT)| { + let mut sut = make(); + setup(&mut sut); + let res = sut.validation_for_addition_of_factor_source_of_kind_to_override(kind); + assert!(res.is_ok()); + }; + let ok = |kind: FactorSourceKind| { + ok_with(kind, |_| {}); + }; + + ok(LedgerHQHardwareWallet); + ok(ArculusCard); + ok(OffDeviceMnemonic); + + ok_with(Device, |sut| { + sut.add_factor_source_to_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + }); + + not_ok(Password); + + not_ok(SecurityQuestions); + not_ok(TrustedContact); + } + } +} + +#[cfg(test)] +mod ledger { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_ledger() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_ledger_other() + } + + #[test] + fn test_suite_prerequisite() { + assert_eq!(sample(), sample()); + assert_eq!(sample_other(), sample_other()); + assert_ne!(sample(), sample_other()); + } + + mod threshold_in_isolation { + use super::*; + fn list() -> FactorListKind { + FactorListKind::Threshold + } + + #[test] + fn duplicates_not_allowed() { + test_duplicates_not_allowed(make(), list(), sample()); + } + + #[test] + fn one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.set_threshold(1).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn one_with_threshold_of_zero_is_err() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build(), + Err(RoleBuilderValidation::NotYetValid( + NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors + )) + ); + } + + #[test] + fn two_different_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + sut.set_threshold(2).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 2, + [sample(), sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + } + + mod override_in_isolation { + use super::*; + fn list() -> FactorListKind { + FactorListKind::Override + } + + #[test] + fn duplicates_not_allowed() { + test_duplicates_not_allowed(make(), list(), sample()); + } + + #[test] + fn one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_override(sample()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 0, + [], + [sample()], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn two_different_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_override(sample()).unwrap(); + sut.add_factor_source_to_override(sample_other()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 0, + [], + [sample(), sample_other()], + ); + assert_eq!(sut.build().unwrap(), expected); + } + } +} + +#[cfg(test)] +mod arculus { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_arculus() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_arculus_other() + } + + #[test] + fn test_suite_prerequisite() { + assert_eq!(sample(), sample()); + assert_eq!(sample_other(), sample_other()); + assert_ne!(sample(), sample_other()); + } + + mod threshold_in_isolation { + use super::*; + fn list() -> FactorListKind { + FactorListKind::Threshold + } + + #[test] + fn duplicates_not_allowed() { + test_duplicates_not_allowed(make(), list(), sample()); + } + + #[test] + fn one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.set_threshold(1).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn two_different_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.add_factor_source_to_threshold(sample_other()).unwrap(); + sut.set_threshold(1).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample(), sample_other()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + } + + mod override_in_isolation { + use super::*; + fn list() -> FactorListKind { + FactorListKind::Override + } + + #[test] + fn duplicates_not_allowed() { + test_duplicates_not_allowed(make(), list(), sample()); + } + + #[test] + fn one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_override(sample()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 0, + [], + [sample()], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn two_different_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_override(sample()).unwrap(); + sut.add_factor_source_to_override(sample_other()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 0, + [], + [sample(), sample_other()], + ); + assert_eq!(sut.build().unwrap(), expected); + } + } +} + +#[cfg(test)] +mod device_factor_source { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_device() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_device_other() + } + + #[test] + fn test_suite_prerequisite() { + assert_eq!(sample(), sample()); + assert_eq!(sample_other(), sample_other()); + assert_ne!(sample(), sample_other()); + } + + #[cfg(test)] + mod threshold_in_isolation { + use super::*; + + fn list() -> FactorListKind { + FactorListKind::Threshold + } + + #[test] + fn duplicates_not_allowed() { + test_duplicates_not_allowed(make(), list(), sample()) + } + + #[test] + fn one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.set_threshold(1).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 1, + [sample()], + [], + ); + assert_eq!(sut.build().unwrap(), expected); + } + + #[test] + fn two_different_is_err() { + // Arrange + let mut sut = make(); + + sut.add_factor_source_to_threshold(sample()).unwrap(); + + // Act + let res = sut.add_factor_source_to_threshold(sample_other()); + + // Assert + assert!(matches!( + res, + MutRes::Err(Validation::ForeverInvalid( + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + )) + )); + } + } + + mod override_in_isolation { + + use super::*; + + fn list() -> FactorListKind { + FactorListKind::Override + } + + #[test] + fn duplicates_not_allowed() { + test_duplicates_not_allowed(make(), list(), sample()) + } + + #[test] + fn one_is_ok() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source_to_override(sample()).unwrap(); + + // Assert + let expected = RoleWithFactorSourceIds::primary_with_factors( + 0, + [], + [sample()], + ); + assert_eq!(sut.build().unwrap(), expected); + } + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs new file mode 100644 index 000000000..9a238b295 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs @@ -0,0 +1,426 @@ +#![cfg(test)] + +use crate::prelude::*; + +type MutRes = RoleBuilderMutateResult; + +#[test] +fn new_builder_recovery() { + assert_eq!(RecoveryRoleBuilder::new().role(), RoleKind::Recovery); +} + +#[test] +fn empty_is_err_recovery() { + let sut = RecoveryRoleBuilder::new(); + let res = sut.build(); + assert_eq!( + res, + Result::not_yet_valid(NotYetValidReason::RoleMustHaveAtLeastOneFactor) + ); +} + +#[allow(clippy::upper_case_acronyms)] +type SUT = RecoveryRoleBuilder; + +fn make() -> SUT { + SUT::new() +} + +fn list() -> FactorListKind { + FactorListKind::Override +} + +#[test] +fn validation_for_addition_of_factor_source_of_kind_to_list() { + use FactorSourceKind::*; + let sut = make(); + let not_ok = |kind: FactorSourceKind| { + let res = sut + .validation_for_addition_of_factor_source_of_kind_to_override(kind); + assert!(res.is_err()); + }; + let ok = |kind: FactorSourceKind| { + let res = sut + .validation_for_addition_of_factor_source_of_kind_to_override(kind); + assert!(res.is_ok()); + }; + ok(Device); + ok(LedgerHQHardwareWallet); + ok(ArculusCard); + ok(TrustedContact); + ok(OffDeviceMnemonic); + + not_ok(Password); + not_ok(SecurityQuestions); +} + +#[test] +fn set_threshold_is_unsupported() { + let mut sut = make(); + assert_eq!( + sut.set_threshold(1), + MutRes::basic_violation(BasicViolation::RecoveryCannotSetThreshold) + ); +} + +mod device_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_device() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_device_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([sample()]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([ + sample(), + sample_other() + ],) + ); + } + + #[test] + fn validation_for_addition_of_factor_source_for_each() { + let sut = make(); + let xs = sut.validation_for_addition_of_factor_source_for_each( + list(), + &IndexSet::from_iter([sample(), sample_other()]), + ); + assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Recovery, + sample() + ), + FactorSourceInRoleBuilderValidationStatus::ok( + RoleKind::Recovery, + sample_other(), + ) + ] + ); + } +} + +mod ledger_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_ledger() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_ledger_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([sample()],) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod arculus_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_arculus() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_arculus_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([sample(),]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod off_device_mnemonic_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_off_device() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_off_device_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([sample()]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod trusted_contact_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_trusted_contact() + } + + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_trusted_contact_other() + } + + #[test] + fn allowed_as_first_and_only() { + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([sample(),]) + ); + } + + #[test] + fn two_of_same_kind_allowed() { + // TODO: Ask Matt + // Arrange + let mut sut = make(); + + // Act + sut.add_factor_source(sample()).unwrap(); + sut.add_factor_source(sample_other()).unwrap(); + + // Assert + assert_eq!( + sut.build().unwrap(), + RoleWithFactorSourceIds::recovery_with_factors([ + sample(), + sample_other() + ]) + ); + } +} + +mod password_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_password() + } + + #[test] + fn unsupported() { + // Arrange + let mut sut = make(); + + // Act + let res = sut.add_factor_source(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::RecoveryRolePasswordNotSupported + ) + ); + } + + #[test] + fn valid_then_invalid_because_unsupported() { + // Arrange + let mut sut = make(); + + sut.add_factor_source(FactorSourceID::sample_ledger()) + .unwrap(); + sut.add_factor_source(FactorSourceID::sample_arculus()) + .unwrap(); + + // Act + let res = sut.add_factor_source(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::RecoveryRolePasswordNotSupported + ) + ); + } +} + +mod security_questions_in_isolation { + use super::*; + + fn sample() -> FactorSourceID { + FactorSourceID::sample_security_questions() + } + fn sample_other() -> FactorSourceID { + FactorSourceID::sample_security_questions_other() + } + + #[test] + fn unsupported() { + // Arrange + let mut sut = make(); + + // Act + let res = sut.add_factor_source(sample()); + + // Assert + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::RecoveryRoleSecurityQuestionsNotSupported + ) + ); + } + + #[test] + fn valid_then_invalid_because_unsupported() { + // Arrange + let mut sut = make(); + + sut.add_factor_source(FactorSourceID::sample_ledger()) + .unwrap(); + sut.add_factor_source(FactorSourceID::sample_arculus()) + .unwrap(); + + // Act + let res = sut.add_factor_source(sample_other()); + + // Assert + let reason = + ForeverInvalidReason::RecoveryRoleSecurityQuestionsNotSupported; + let err = MutRes::forever_invalid(reason); + assert_eq!(res, err); + + // .. erroneous action above did not change the state of the builder (SUT), + // so we can build and `sample` is not present in the built result. + assert_eq!( + sut.build(), + Ok(RoleWithFactorSourceIds::recovery_with_factors([ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_arculus() + ])) + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs new file mode 100644 index 000000000..996036e4a --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -0,0 +1,857 @@ +use crate::prelude::*; + +use FactorListKind::*; + +pub type PrimaryRoleBuilder = RoleBuilder<{ ROLE_PRIMARY }>; +pub type RecoveryRoleBuilder = RoleBuilder<{ ROLE_RECOVERY }>; +pub type ConfirmationRoleBuilder = RoleBuilder<{ ROLE_CONFIRMATION }>; + +#[cfg(test)] +impl PrimaryRoleWithFactorSourceIds { + pub(crate) fn primary_with_factors( + threshold: u8, + threshold_factors: impl IntoIterator, + override_factors: impl IntoIterator, + ) -> Self { + Self::with_factors(threshold, threshold_factors, override_factors) + } +} + +#[cfg(test)] +impl RecoveryRoleWithFactorSourceIds { + pub(crate) fn recovery_with_factors( + override_factors: impl IntoIterator, + ) -> Self { + Self::with_factors(0, vec![], override_factors) + } +} + +#[cfg(test)] +impl ConfirmationRoleWithFactorSourceIds { + pub(crate) fn confirmation_with_factors( + override_factors: impl IntoIterator, + ) -> Self { + Self::with_factors(0, vec![], override_factors) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum RoleBuilderValidation { + #[error("Basic violation: {0}")] + BasicViolation(#[from] BasicViolation), + + #[error("Forever invalid: {0}")] + ForeverInvalid(#[from] ForeverInvalidReason), + + #[error("Not yet valid: {0}")] + NotYetValid(#[from] NotYetValidReason), +} +use RoleBuilderValidation::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum BasicViolation { + /// e.g. tried to remove a factor source which was not found. + #[error("FactorSourceID not found")] + FactorSourceNotFound, + + #[error("Recovery cannot set threshold")] + RecoveryCannotSetThreshold, + + #[error("Confirmation cannot set threshold")] + ConfirmationCannotSetThreshold, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum NotYetValidReason { + #[error("Role must have at least one factor")] + RoleMustHaveAtLeastOneFactor, + + #[error( + "Primary role with password in threshold list must have another factor" + )] + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + + #[error( + "Primary role with threshold factors cannot have a threshold of zero" + )] + PrimaryRoleWithThresholdCannotBeZeroWithFactors, + + #[error("Primary role with password in threshold list must have threshold greater than one")] + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + + #[error("Threshold higher than threshold factors len")] + ThresholdHigherThanThresholdFactorsLen, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +pub enum ForeverInvalidReason { + #[error("Factor source already present")] + FactorSourceAlreadyPresent, + + #[error("Primary role cannot have multiple devices")] + PrimaryCannotHaveMultipleDevices, + + #[error("Primary role cannot have password in override list")] + PrimaryCannotHavePasswordInOverrideList, + + #[error("Primary role cannot contain Security Questions")] + PrimaryCannotContainSecurityQuestions, + + #[error("Primary role cannot contain Trusted Contact")] + PrimaryCannotContainTrustedContact, + + #[error("Recovery role threshold list not supported")] + RecoveryRoleThresholdFactorsNotSupported, + + #[error("Recovery role Security Questions not supported")] + RecoveryRoleSecurityQuestionsNotSupported, + + #[error("Recovery role password not supported")] + RecoveryRolePasswordNotSupported, + + #[error("Confirmation role threshold list not supported")] + ConfirmationRoleThresholdFactorsNotSupported, + + #[error("Confirmation role cannot contain Trusted Contact")] + ConfirmationRoleTrustedContactNotSupported, +} + +pub(crate) trait FromForeverInvalid { + fn forever_invalid(reason: ForeverInvalidReason) -> Self; +} +impl FromForeverInvalid for std::result::Result { + fn forever_invalid(reason: ForeverInvalidReason) -> Self { + Err(ForeverInvalid(reason)) + } +} + +pub(crate) trait FromNotYetValid { + fn not_yet_valid(reason: NotYetValidReason) -> Self; +} +impl FromNotYetValid for std::result::Result { + fn not_yet_valid(reason: NotYetValidReason) -> Self { + Err(NotYetValid(reason)) + } +} + +pub(crate) trait FromBasicViolation { + fn basic_violation(reason: BasicViolation) -> Self; +} +impl FromBasicViolation for std::result::Result { + fn basic_violation(reason: BasicViolation) -> Self { + Err(BasicViolation(reason)) + } +} + +impl ForeverInvalidReason { + pub(crate) fn threshold_list_not_supported_for_role( + role: RoleKind, + ) -> Self { + match role { + RoleKind::Recovery => { + Self::RecoveryRoleThresholdFactorsNotSupported + } + RoleKind::Confirmation => { + Self::ConfirmationRoleThresholdFactorsNotSupported + } + RoleKind::Primary => { + unreachable!("Primary role DOES support threshold list. This is programmer error.") + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FactorSourceInRoleBuilderValidationStatus { + pub role: RoleKind, + pub factor_source_id: FactorSourceID, + pub validation: RoleBuilderMutateResult, +} + +impl FactorSourceInRoleBuilderValidationStatus { + pub(crate) fn new( + role: RoleKind, + factor_source_id: FactorSourceID, + validation: RoleBuilderMutateResult, + ) -> Self { + Self { + role, + factor_source_id, + validation, + } + } +} + +#[cfg(test)] +impl FactorSourceInRoleBuilderValidationStatus { + pub(crate) fn ok(role: RoleKind, factor_source_id: FactorSourceID) -> Self { + Self::new(role, factor_source_id, Ok(())) + } + + pub(crate) fn forever_invalid( + role: RoleKind, + factor_source_id: FactorSourceID, + reason: ForeverInvalidReason, + ) -> Self { + Self::new( + role, + factor_source_id, + RoleBuilderMutateResult::forever_invalid(reason), + ) + } + + pub(crate) fn not_yet_valid( + role: RoleKind, + factor_source_id: FactorSourceID, + reason: NotYetValidReason, + ) -> Self { + Self::new( + role, + factor_source_id, + RoleBuilderMutateResult::not_yet_valid(reason), + ) + } +} + +use BasicViolation::*; +use ForeverInvalidReason::*; +use NotYetValidReason::*; +use RoleKind::*; + +pub type RoleBuilderMutateResult = Result<(), RoleBuilderValidation>; + +pub enum Assert {} +pub trait IsTrue {} +impl IsTrue for Assert {} + +impl RoleBuilder +where + Assert<{ R == ROLE_PRIMARY }>: IsTrue, +{ + /// If Ok => self is mutated + /// If Err(NotYetValid) => self is mutated + /// If Err(ForeverInvalid) => self is not mutated + pub(crate) fn add_factor_source_to_threshold( + &mut self, + factor_source_id: FactorSourceID, + ) -> RoleBuilderMutateResult { + self._add_factor_source_to_list(factor_source_id, Threshold) + } + + /// If we would add a factor of kind `factor_source_kind` to the list of kind `factor_list_kind` + /// what would be the validation status? + pub(crate) fn validation_for_addition_of_factor_source_of_kind_to_threshold( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self._validation_add(factor_source_kind, Threshold) + } + + #[cfg(test)] + pub(crate) fn validation_for_addition_of_factor_source_of_kind_to_list( + &self, + factor_source_kind: FactorSourceKind, + list: FactorListKind, + ) -> RoleBuilderMutateResult { + self._validation_add(factor_source_kind, list) + } +} + +impl RoleBuilder +where + Assert<{ R > ROLE_PRIMARY }>: IsTrue, +{ + /// If Ok => self is mutated + /// If Err(NotYetValid) => self is mutated + /// If Err(ForeverInvalid) => self is not mutated + pub(crate) fn add_factor_source( + &mut self, + factor_source_id: FactorSourceID, + ) -> RoleBuilderMutateResult { + self.add_factor_source_to_override(factor_source_id) + } +} + +impl RoleBuilder { + /// If Ok => self is mutated + /// If Err(NotYetValid) => self is mutated + /// If Err(ForeverInvalid) => self is not mutated + pub(crate) fn add_factor_source_to_override( + &mut self, + factor_source_id: FactorSourceID, + ) -> RoleBuilderMutateResult { + self._add_factor_source_to_list(factor_source_id, Override) + } + + /// If Ok => self is mutated + /// If Err(NotYetValid) => self is mutated + /// If Err(ForeverInvalid) => self is not mutated + fn _add_factor_source_to_list( + &mut self, + factor_source_id: FactorSourceID, + factor_list_kind: FactorListKind, + ) -> RoleBuilderMutateResult { + let validation = self.validation_for_addition_of_factor_source_to_list( + &factor_source_id, + factor_list_kind, + ); + match validation.as_ref() { + Ok(()) | Err(NotYetValid(_)) => { + self.unchecked_add_factor_source_to_list( + factor_source_id, + factor_list_kind, + ); + } + Err(ForeverInvalid(_)) | Err(BasicViolation(_)) => {} + } + validation + } + + /// If we would add a factor of kind `factor_source_kind` to the list of kind `factor_list_kind` + /// what would be the validation status? + pub(crate) fn validation_for_addition_of_factor_source_of_kind_to_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self._validation_add(factor_source_kind, Override) + } + + /// If we would add a factor of kind `factor_source_kind` to the list of kind `factor_list_kind` + /// what would be the validation status? + fn _validation_add( + &self, + factor_source_kind: FactorSourceKind, + factor_list_kind: FactorListKind, + ) -> RoleBuilderMutateResult { + match self.role() { + RoleKind::Primary => { + return self.validation_for_addition_of_factor_source_of_kind_to_list_for_primary( + factor_source_kind, + factor_list_kind, + ) + } + RoleKind::Recovery | RoleKind::Confirmation => match factor_list_kind { + Threshold => { + return Result::forever_invalid( + ForeverInvalidReason::threshold_list_not_supported_for_role(self.role()), + ) + } + Override => {} + }, + } + self.validation_for_addition_of_factor_source_of_kind_to_override_for_non_primary_role( + factor_source_kind, + ) + } +} + +impl RoleBuilder { + pub(crate) fn build( + self, + ) -> Result, RoleBuilderValidation> { + self.validate().map(|_| { + RoleWithFactorSourceIds::with_factors( + self.get_threshold(), + self.get_threshold_factors().clone(), + self.get_override_factors().clone(), + ) + }) + } + + #[allow(dead_code)] + pub(crate) fn set_threshold( + &mut self, + threshold: u8, + ) -> RoleBuilderMutateResult { + match self.role() { + Primary => { + self.unchecked_set_threshold(threshold); + self.validate() + } + Recovery => RoleBuilderMutateResult::basic_violation( + RecoveryCannotSetThreshold, + ), + Confirmation => RoleBuilderMutateResult::basic_violation( + ConfirmationCannotSetThreshold, + ), + } + } + + fn override_contains_factor_source( + &self, + factor_source_id: &FactorSourceID, + ) -> bool { + self.get_override_factors().contains(factor_source_id) + } + + fn threshold_contains_factor_source( + &self, + factor_source_id: &FactorSourceID, + ) -> bool { + self.get_threshold_factors().contains(factor_source_id) + } + + fn override_contains_factor_source_of_kind( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get_override_factors() + .iter() + .any(|f| f.get_factor_source_kind() == factor_source_kind) + } + + fn threshold_contains_factor_source_of_kind( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get_threshold_factors() + .iter() + .any(|f| f.get_factor_source_kind() == factor_source_kind) + } + + /// Validates `self` by "replaying" the addition of each factor source in `self` to a + /// "simulation" (clone). If the simulation is valid, then `self` is valid. + pub(crate) fn validate(&self) -> RoleBuilderMutateResult { + let mut simulation = Self::new(); + + // Validate override factors + for f in self.get_override_factors() { + let validation = simulation.add_factor_source_to_override(*f); + match validation.as_ref() { + Ok(()) | Err(NotYetValid(_)) => continue, + Err(ForeverInvalid(_)) | Err(BasicViolation(_)) => { + return validation + } + } + } + + // Validate threshold factors + for f in self.get_threshold_factors() { + let validation = + simulation._add_factor_source_to_list(*f, Threshold); + match validation.as_ref() { + Ok(()) | Err(NotYetValid(_)) => continue, + Err(ForeverInvalid(_)) | Err(BasicViolation(_)) => { + return validation + } + } + } + + // Validate threshold count + if self.role() == RoleKind::Primary { + if self.get_threshold_factors().len() + < self.get_threshold() as usize + { + return RoleBuilderMutateResult::not_yet_valid( + NotYetValidReason::ThresholdHigherThanThresholdFactorsLen, + ); + } + if self.get_threshold() == 0 + && !self.get_threshold_factors().is_empty() + { + return RoleBuilderMutateResult::not_yet_valid( + NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors, + ); + } + } else if self.get_threshold() != 0 { + match self.role() { + Primary => unreachable!( + "Primary role should have been handled earlier" + ), + Recovery => { + return RoleBuilderMutateResult::basic_violation( + RecoveryCannotSetThreshold, + ) + } + Confirmation => { + return RoleBuilderMutateResult::basic_violation( + ConfirmationCannotSetThreshold, + ) + } + } + } + + if self.all_factors().is_empty() { + return RoleBuilderMutateResult::not_yet_valid( + RoleMustHaveAtLeastOneFactor, + ); + } + + Ok(()) + } + + fn validation_for_addition_of_factor_source_of_kind_to_override_for_non_primary_role( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + match self.role() { + RoleKind::Primary => { + unreachable!("Should have branched to 'primary' earlier, this is programmer error.") + } + RoleKind::Confirmation => self + .validation_for_addition_of_factor_source_of_kind_to_override_for_confirmation( + factor_source_kind, + ), + RoleKind::Recovery => self + .validation_for_addition_of_factor_source_of_kind_to_override_for_recovery( + factor_source_kind, + ), + } + } + + #[allow(dead_code)] + /// For each factor source in the given set, return a validation status + /// for adding it to factor list of the given kind (`factor_list_kind`) + pub(crate) fn validation_for_addition_of_factor_source_for_each( + &self, + factor_list_kind: FactorListKind, + factor_sources: &IndexSet, + ) -> IndexSet { + factor_sources + .iter() + .map(|factor_source_id| { + let validation_status = self + .validation_for_addition_of_factor_source_to_list( + factor_source_id, + factor_list_kind, + ); + FactorSourceInRoleBuilderValidationStatus::new( + self.role(), + *factor_source_id, + validation_status, + ) + }) + .collect() + } + + fn validation_for_addition_of_factor_source_to_list( + &self, + factor_source_id: &FactorSourceID, + factor_list_kind: FactorListKind, + ) -> RoleBuilderMutateResult { + if self.contains_factor_source(factor_source_id) { + return RoleBuilderMutateResult::forever_invalid( + FactorSourceAlreadyPresent, + ); + } + let factor_source_kind = factor_source_id.get_factor_source_kind(); + self._validation_add(factor_source_kind, factor_list_kind) + } + + fn contains_factor_source( + &self, + factor_source_id: &FactorSourceID, + ) -> bool { + self.override_contains_factor_source(factor_source_id) + || self.threshold_contains_factor_source(factor_source_id) + } + + fn contains_factor_source_of_kind( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.override_contains_factor_source_of_kind(factor_source_kind) + || self.threshold_contains_factor_source_of_kind(factor_source_kind) + } + + /// Lowers the threshold if the deleted factor source is in the threshold list + /// and if after removal of `factor_source_id` `self.threshold > self.threshold_factors.len()` + /// + /// Returns `Ok` if `factor_source_id` was found and deleted. However, does not call `self.validate()`, + /// So state might still be invalid, i.e. we return the result of the action of removal, not the + /// state validation status. + pub(crate) fn remove_factor_source( + &mut self, + factor_source_id: &FactorSourceID, + ) -> RoleBuilderMutateResult { + if !self.contains_factor_source(factor_source_id) { + return RoleBuilderMutateResult::basic_violation( + FactorSourceNotFound, + ); + } + let remove = |xs: &mut Vec| { + let index = xs + .iter() + .position(|f| f == factor_source_id) + .expect("Called remove of non existing FactorSourceID, this is a programmer error, should have checked if it exists before calling remove."); + xs.remove(index); + }; + + if self.override_contains_factor_source(factor_source_id) { + remove(self.mut_override_factors()) + } + if self.threshold_contains_factor_source(factor_source_id) { + remove(self.mut_threshold_factors()); + let threshold_factors_len = + self.get_threshold_factors().len() as u8; + if self.get_threshold() > threshold_factors_len { + self.set_threshold(threshold_factors_len)?; + } + } + + Ok(()) + } + + #[cfg(not(tarpaulin_include))] // false negative + fn validation_for_addition_of_factor_source_of_kind_to_list_for_primary( + &self, + factor_source_kind: FactorSourceKind, + factor_list_kind: FactorListKind, + ) -> RoleBuilderMutateResult { + match factor_source_kind { + FactorSourceKind::Password => { + return self.validation_for_addition_of_password_to_primary( + factor_list_kind, + ) + } + FactorSourceKind::SecurityQuestions => { + return RoleBuilderMutateResult::forever_invalid( + PrimaryCannotContainSecurityQuestions, + ); + } + FactorSourceKind::TrustedContact => { + return RoleBuilderMutateResult::forever_invalid( + PrimaryCannotContainTrustedContact, + ); + } + FactorSourceKind::Device => { + if self.contains_factor_source_of_kind(FactorSourceKind::Device) + { + return RoleBuilderMutateResult::forever_invalid( + PrimaryCannotHaveMultipleDevices, + ); + } + } + FactorSourceKind::LedgerHQHardwareWallet + | FactorSourceKind::ArculusCard + | FactorSourceKind::OffDeviceMnemonic => {} + } + Ok(()) + } + + #[cfg(not(tarpaulin_include))] // false negative + fn validation_for_addition_of_factor_source_of_kind_to_override_for_confirmation( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + assert_eq!(self.role(), RoleKind::Confirmation); + match factor_source_kind { + FactorSourceKind::Device + | FactorSourceKind::LedgerHQHardwareWallet + | FactorSourceKind::ArculusCard + | FactorSourceKind::Password + | FactorSourceKind::OffDeviceMnemonic + | FactorSourceKind::SecurityQuestions => Ok(()), + FactorSourceKind::TrustedContact => { + RoleBuilderMutateResult::forever_invalid( + ConfirmationRoleTrustedContactNotSupported, + ) + } + } + } + + #[cfg(not(tarpaulin_include))] // false negative + fn validation_for_addition_of_factor_source_of_kind_to_override_for_recovery( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + assert_eq!(self.role(), RoleKind::Recovery); + match factor_source_kind { + FactorSourceKind::Device + | FactorSourceKind::LedgerHQHardwareWallet + | FactorSourceKind::ArculusCard + | FactorSourceKind::OffDeviceMnemonic + | FactorSourceKind::TrustedContact => Ok(()), + FactorSourceKind::SecurityQuestions => { + RoleBuilderMutateResult::forever_invalid( + RecoveryRoleSecurityQuestionsNotSupported, + ) + } + FactorSourceKind::Password => { + RoleBuilderMutateResult::forever_invalid( + RecoveryRolePasswordNotSupported, + ) + } + } + } +} + +// ======================= +// ======== RULES ======== +// ======================= +impl RoleBuilder { + fn validation_for_addition_of_password_to_primary( + &self, + factor_list_kind: FactorListKind, + ) -> RoleBuilderMutateResult { + assert_eq!(self.role(), RoleKind::Primary); + let factor_source_kind = FactorSourceKind::Password; + match factor_list_kind { + Threshold => { + let is_alone = self + .factor_sources_not_of_kind_to_list_of_kind( + factor_source_kind, + Threshold, + ) + .is_empty(); + if is_alone { + return RoleBuilderMutateResult::not_yet_valid( + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + ); + } + if self.get_threshold() < 2 { + return RoleBuilderMutateResult::not_yet_valid( + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + ); + } + } + Override => { + return RoleBuilderMutateResult::forever_invalid( + PrimaryCannotHavePasswordInOverrideList, + ); + } + } + + Ok(()) + } + + pub(crate) fn factor_sources_not_of_kind_to_list_of_kind( + &self, + factor_source_kind: FactorSourceKind, + factor_list_kind: FactorListKind, + ) -> Vec { + let filter = |xs: &Vec| -> Vec { + xs.iter() + .filter(|f| f.get_factor_source_kind() != factor_source_kind) + .cloned() + .collect() + }; + match factor_list_kind { + Override => filter(self.get_override_factors()), + Threshold => filter(self.get_threshold_factors()), + } + } +} + +#[cfg(test)] +pub(crate) fn test_duplicates_not_allowed( + sut: RoleBuilder, + list: FactorListKind, + factor_source_id: FactorSourceID, +) { + // Arrange + let mut sut = sut; + + sut._add_factor_source_to_list(factor_source_id, list) + .unwrap(); + + // Act + let res = sut._add_factor_source_to_list( + factor_source_id, // oh no, duplicate! + list, + ); + + // Assert + assert!(matches!( + res, + RoleBuilderMutateResult::Err(ForeverInvalid( + ForeverInvalidReason::FactorSourceAlreadyPresent + )) + )); +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn primary_duplicates_not_allowed() { + test_duplicates_not_allowed( + PrimaryRoleBuilder::new(), + Override, + FactorSourceID::sample_arculus(), + ); + test_duplicates_not_allowed( + PrimaryRoleBuilder::new(), + Threshold, + FactorSourceID::sample_arculus(), + ); + } + + #[test] + fn recovery_duplicates_not_allowed() { + test_duplicates_not_allowed( + RecoveryRoleBuilder::new(), + Override, + FactorSourceID::sample_arculus(), + ); + } + + #[test] + fn confirmation_duplicates_not_allowed() { + test_duplicates_not_allowed( + ConfirmationRoleBuilder::new(), + Override, + FactorSourceID::sample_arculus(), + ); + } + + #[test] + fn recovery_cannot_add_factors_to_threshold() { + let mut sut = RecoveryRoleBuilder::new(); + let res = sut._add_factor_source_to_list( + FactorSourceID::sample_ledger(), + Threshold, + ); + assert_eq!( + res, + Err(ForeverInvalid( + ForeverInvalidReason::RecoveryRoleThresholdFactorsNotSupported + )) + ); + } + + #[test] + fn confirmation_cannot_add_factors_to_threshold() { + let mut sut = ConfirmationRoleBuilder::new(); + let res = sut._add_factor_source_to_list( + FactorSourceID::sample_ledger(), + Threshold, + ); + assert_eq!( + res, + Err(ForeverInvalid( + ForeverInvalidReason::ConfirmationRoleThresholdFactorsNotSupported + )) + ); + } + + #[test] + fn recovery_validation_add_is_err_for_threshold() { + let sut = RecoveryRoleBuilder::new(); + let res = sut._validation_add(FactorSourceKind::Device, Threshold); + assert_eq!( + res, + RoleBuilderMutateResult::forever_invalid( + ForeverInvalidReason::threshold_list_not_supported_for_role( + RoleKind::Recovery + ) + ) + ); + } + + #[test] + fn confirmation_validation_add_is_err_for_threshold() { + let sut = ConfirmationRoleBuilder::new(); + let res = sut._validation_add(FactorSourceKind::Device, Threshold); + assert_eq!( + res, + RoleBuilderMutateResult::forever_invalid( + ForeverInvalidReason::threshold_list_not_supported_for_role( + RoleKind::Confirmation + ) + ) + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder_unit_tests.rs new file mode 100644 index 000000000..2672bcee6 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder_unit_tests.rs @@ -0,0 +1,88 @@ +#![cfg(test)] + +use crate::prelude::*; + +use NotYetValidReason::*; + +type MutRes = RoleBuilderMutateResult; + +#[test] +fn validate_override_for_ever_invalid() { + let sut = PrimaryRoleBuilder::with_factors( + 0, + vec![], + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger(), + ], + ); + let res = sut.validate(); + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::FactorSourceAlreadyPresent + ) + ); +} + +#[test] +fn validate_threshold_for_ever_invalid() { + let sut = PrimaryRoleBuilder::with_factors( + 1, + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger(), + ], + vec![], + ); + let res = sut.validate(); + assert_eq!( + res, + MutRes::forever_invalid( + ForeverInvalidReason::FactorSourceAlreadyPresent + ) + ); +} + +#[test] +fn confirmation_validate_basic_violation() { + let sut = ConfirmationRoleBuilder::with_factors( + 1, + vec![], + vec![FactorSourceID::sample_ledger()], + ); + let res = sut.validate(); + assert_eq!( + res, + MutRes::basic_violation(BasicViolation::ConfirmationCannotSetThreshold) + ); +} + +#[test] +fn recovery_validate_basic_violation() { + let sut = RecoveryRoleBuilder::with_factors( + 1, + vec![], + vec![FactorSourceID::sample_ledger()], + ); + let res = sut.validate(); + assert_eq!( + res, + MutRes::basic_violation(BasicViolation::RecoveryCannotSetThreshold) + ); +} + +#[test] +fn primary_validate_not_yet_valid_for_threshold_greater_than_threshold_factors() +{ + let sut = PrimaryRoleBuilder::with_factors( + 1, + vec![], + vec![FactorSourceID::sample_ledger()], + ); + let res = sut.validate(); + assert_eq!( + res, + MutRes::not_yet_valid(ThresholdHigherThanThresholdFactorsLen) + ); +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs new file mode 100644 index 000000000..179d18f1c --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs @@ -0,0 +1,33 @@ +use crate::prelude::*; + +pub(crate) type ConfirmationRoleWithFactorInstances = + RoleWithFactorInstances<{ ROLE_CONFIRMATION }>; + +impl HasSampleValues for ConfirmationRoleWithFactorInstances { + fn sample() -> Self { + MatrixOfFactorInstances::sample().confirmation_role + } + + fn sample_other() -> Self { + MatrixOfFactorInstances::sample_other().confirmation_role + } +} + +#[cfg(test)] +mod confirmation_tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = ConfirmationRoleWithFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/general_role_with_hierarchical_deterministic_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/general_role_with_hierarchical_deterministic_factor_instances.rs new file mode 100644 index 000000000..57a7b1e04 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/general_role_with_hierarchical_deterministic_factor_instances.rs @@ -0,0 +1,295 @@ +use crate::prelude::*; + +/// A general depiction of each of the roles in a `MatrixOfFactorInstances`. +/// `SignaturesCollector` can work on any `RoleKind` when dealing with a securified entity. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[serde(rename_all = "camelCase")] +pub struct GeneralRoleWithHierarchicalDeterministicFactorInstances { + role: RoleKind, + threshold: u8, + threshold_factors: Vec, + override_factors: Vec, +} + +impl GeneralRoleWithHierarchicalDeterministicFactorInstances { + pub fn get_threshold(&self) -> u8 { + self.threshold + } + + pub fn get_threshold_factors( + &self, + ) -> Vec { + self.threshold_factors.clone() + } + + pub fn get_override_factors( + &self, + ) -> Vec { + self.override_factors.clone() + } + + pub fn with_factors_and_role( + role: RoleKind, + threshold_factors: impl IntoIterator< + Item = HierarchicalDeterministicFactorInstance, + >, + threshold: u8, + override_factors: impl IntoIterator< + Item = HierarchicalDeterministicFactorInstance, + >, + ) -> Result { + let threshold_factors = threshold_factors.into_iter().collect_vec(); + let override_factors = override_factors.into_iter().collect_vec(); + + // validate + let _ = PrimaryRoleWithFactorInstances::with_factors( + threshold, + threshold_factors + .clone() + .into_iter() + .map(FactorInstance::from) + .collect_vec(), + override_factors + .clone() + .into_iter() + .map(FactorInstance::from) + .collect_vec(), + ); + + Ok(Self { + role, + threshold, + threshold_factors, + override_factors, + }) + } +} + +impl HasRoleKindObjectSafe + for GeneralRoleWithHierarchicalDeterministicFactorInstances +{ + fn get_role_kind(&self) -> RoleKind { + self.role + } +} + +impl TryFrom<(MatrixOfFactorInstances, RoleKind)> + for GeneralRoleWithHierarchicalDeterministicFactorInstances +{ + type Error = CommonError; + + fn try_from( + (matrix, role_kind): (MatrixOfFactorInstances, RoleKind), + ) -> Result { + let threshold_factors: Vec; + let override_factors: Vec; + let threshold: u8; + + match role_kind { + RoleKind::Primary => { + let role = matrix.primary(); + threshold = role.get_threshold(); + threshold_factors = role.get_threshold_factors().clone(); + override_factors = role.get_override_factors().clone(); + } + RoleKind::Recovery => { + let role = matrix.recovery(); + threshold = role.get_threshold(); + threshold_factors = role.get_threshold_factors().clone(); + override_factors = role.get_override_factors().clone(); + } + RoleKind::Confirmation => { + let role = matrix.confirmation(); + threshold = role.get_threshold(); + threshold_factors = role.get_threshold_factors().clone(); + override_factors = role.get_override_factors().clone(); + } + } + + Self::with_factors_and_role( + role_kind, + threshold_factors + .iter() + .map(|f| { + HierarchicalDeterministicFactorInstance::try_from_factor_instance(f.clone()) + }) + .collect::, CommonError>>()?, + threshold, + override_factors + .iter() + .map(|f| { + HierarchicalDeterministicFactorInstance::try_from_factor_instance(f.clone()) + }) + .collect::, CommonError>>()?, + ) + } +} + +impl GeneralRoleWithHierarchicalDeterministicFactorInstances { + pub fn single_override( + role: RoleKind, + factor: HierarchicalDeterministicFactorInstance, + ) -> Self { + assert!(factor.is_securified(), "non securified factor"); + Self::with_factors_and_role(role, [], 0, [factor]) + .expect("Zero threshold with zero threshold factors and one override should not fail.") + } + + pub fn single_threshold( + role: RoleKind, + factor: HierarchicalDeterministicFactorInstance, + ) -> Self { + assert!(factor.is_securified(), "non securified factor"); + Self::with_factors_and_role(role, [factor], 1, []).expect( + "Single threshold with one threshold factor should not fail.", + ) + } +} + +impl HasSampleValues + for GeneralRoleWithHierarchicalDeterministicFactorInstances +{ + fn sample() -> Self { + Self::try_from((MatrixOfFactorInstances::sample(), RoleKind::Primary)) + .expect("Sample should not fail") + } + + fn sample_other() -> Self { + Self::try_from(( + MatrixOfFactorInstances::sample_other(), + RoleKind::Recovery, + )) + .expect("Sample should not fail") + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = GeneralRoleWithHierarchicalDeterministicFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + fn matrix() -> MatrixOfFactorInstances { + MatrixOfFactorInstances::sample() + } + + #[test] + fn test_from_primary_role() { + pretty_assertions::assert_eq!( + SUT::try_from( + (matrix(), RoleKind::Primary) + ).unwrap(), + SUT::with_factors_and_role( + RoleKind::Primary, + [ + HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(0), + HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_1_securified_at_index(0) + ], + 2, + [] + ).unwrap() + ) + } + + #[test] + fn test_single_threshold() { + pretty_assertions::assert_eq!( + SUT::single_threshold(RoleKind::Primary, HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_1_securified_at_index(0)), + SUT::with_factors_and_role( + RoleKind::Primary, + [ + HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_1_securified_at_index(0) + ], + 1, + [] + ).unwrap() + ) + } + + #[test] + fn test_get_role() { + let test = |role: RoleKind| { + let sut = SUT::single_override( + role, + HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(0) + ); + assert_eq!(sut.get_role_kind(), role); + }; + test(RoleKind::Primary); + test(RoleKind::Confirmation); + test(RoleKind::Recovery); + } + + #[test] + fn test_from_recovery_role() { + let m = matrix(); + let r = m.recovery(); + assert_eq!( + SUT::try_from((matrix(), RoleKind::Recovery)).unwrap(), + SUT::with_factors_and_role( + RoleKind::Recovery, + [], + 0, + r.get_override_factors() + .clone() + .into_iter() + .map(|f: FactorInstance| { + HierarchicalDeterministicFactorInstance::try_from_factor_instance(f) + .unwrap() + }) + .collect_vec(), + ) + .unwrap() + ) + } + + #[test] + fn test_from_confirmation_role() { + let m = matrix(); + let r = m.confirmation(); + assert_eq!( + SUT::try_from((matrix(), RoleKind::Confirmation)).unwrap(), + SUT::with_factors_and_role( + RoleKind::Confirmation, + [], + 0, + r.get_override_factors() + .clone() + .into_iter() + .map(|f: FactorInstance| { + HierarchicalDeterministicFactorInstance::try_from_factor_instance(f) + .unwrap() + }) + .collect_vec(), + ) + .unwrap() + ) + } + + #[test] + fn test_from_matrix_containing_physical_badge() { + let mut matrix = MatrixOfFactorInstances::sample(); + matrix.primary_role = PrimaryRoleWithFactorInstances::with_factors( + 0, + [], + [FactorInstance::sample_other()], + ); + + assert_eq!( + SUT::try_from((matrix, RoleKind::Primary)), + Err(CommonError::BadgeIsNotVirtualHierarchicalDeterministic) + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs new file mode 100644 index 000000000..ca98a01d5 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs @@ -0,0 +1,11 @@ +mod confirmation_role_with_factor_instances; +mod general_role_with_hierarchical_deterministic_factor_instances; +mod primary_role_with_factor_instances; +mod recovery_role_with_factor_instances; +mod role_with_factor_instances; + +pub(crate) use confirmation_role_with_factor_instances::*; +pub use general_role_with_hierarchical_deterministic_factor_instances::*; +pub(crate) use primary_role_with_factor_instances::*; +pub(crate) use recovery_role_with_factor_instances::*; +pub(crate) use role_with_factor_instances::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs new file mode 100644 index 000000000..16bfec6ae --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs @@ -0,0 +1,111 @@ +use crate::prelude::*; + +pub(crate) type PrimaryRoleWithFactorInstances = + RoleWithFactorInstances<{ ROLE_PRIMARY }>; + +impl HasSampleValues for PrimaryRoleWithFactorInstances { + fn sample() -> Self { + MatrixOfFactorInstances::sample().primary_role + } + + fn sample_other() -> Self { + MatrixOfFactorInstances::sample_other().primary_role + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleWithFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + #[should_panic] + fn primary_role_non_securified_threshold_instances_is_err() { + let _ = SUT::with_factors( + 1, + [ + HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_unsecurified_at_index(0).into() + ], + [] + ); + } + + #[test] + fn assert_json_sample() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 2, + "thresholdFactors": [ + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "427969814e15d74c3ff4d9971465cb709d210c8a7627af9466bdaa67bd0929b7" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + }, + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + } + ], + "overrideFactors": [] + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs new file mode 100644 index 000000000..02795e935 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs @@ -0,0 +1,33 @@ +use crate::prelude::*; + +pub(crate) type RecoveryRoleWithFactorInstances = + RoleWithFactorInstances<{ ROLE_RECOVERY }>; + +impl HasSampleValues for RecoveryRoleWithFactorInstances { + fn sample() -> Self { + MatrixOfFactorInstances::sample().recovery_role + } + + fn sample_other() -> Self { + MatrixOfFactorInstances::sample_other().recovery_role + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = RecoveryRoleWithFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs new file mode 100644 index 000000000..978b98818 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs @@ -0,0 +1,79 @@ +use crate::prelude::*; + +pub(crate) type RoleWithFactorInstances = + AbstractBuiltRoleWithFactor; + +impl RoleWithFactorSources { + fn from(other: &RoleWithFactorSources) -> Self { + Self::with_factors( + other.get_threshold(), + other.get_threshold_factors().clone(), + other.get_override_factors().clone(), + ) + } +} + +impl MatrixOfFactorSources { + pub(crate) fn get_role(&self) -> RoleWithFactorSources { + match R { + ROLE_PRIMARY => RoleWithFactorSources::from(&self.primary_role), + ROLE_RECOVERY => RoleWithFactorSources::from(&self.recovery_role), + ROLE_CONFIRMATION => { + RoleWithFactorSources::from(&self.confirmation_role) + } + _ => panic!("unknown"), + } + } +} + +impl RoleWithFactorInstances { + pub(crate) fn fulfilling_role_of_factor_sources_with_factor_instances( + consuming_instances: &IndexMap, + matrix_of_factor_sources: &MatrixOfFactorSources, + ) -> Result { + let role_kind = RoleKind::from_u8(R).unwrap(); + + let role_of_sources = matrix_of_factor_sources.get_role::(); + assert_eq!(role_of_sources.role(), role_kind); + let threshold: u8 = role_of_sources.get_threshold(); + + // Threshold factors + let threshold_factors = + Self::try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( + consuming_instances, + role_of_sources.get_threshold_factors(), + )?; + + // Override factors + let override_factors = + Self::try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( + consuming_instances, + role_of_sources.get_override_factors(), + )?; + + let role_with_instances = + Self::with_factors(threshold, threshold_factors, override_factors); + + assert_eq!(role_with_instances.role(), role_kind); + Ok(role_with_instances) + } + + fn try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( + instances: &IndexMap, + from: &[FactorSource], + ) -> Result, CommonError> { + from.iter() + .map(|f| { + if let Some(existing) = instances.get(&f.id_from_hash()) { + let hd_instance = existing.first().ok_or( + CommonError::MissingFactorMappingInstancesIntoRole, + )?; + let instance = FactorInstance::from(hd_instance); + Ok(instance) + } else { + Err(CommonError::MissingFactorMappingInstancesIntoRole) + } + }) + .collect::, CommonError>>() + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs new file mode 100644 index 000000000..38b69a1ec --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs @@ -0,0 +1,102 @@ +use crate::prelude::*; + +pub type ConfirmationRoleWithFactorSourceIds = + RoleWithFactorSourceIds<{ ROLE_CONFIRMATION }>; + +impl HasSampleValues for ConfirmationRoleWithFactorSourceIds { + /// Config MFA 1.1 + fn sample() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_password()) + .unwrap(); + builder.build().unwrap() + } + + /// Config MFA 2.1 + fn sample_other() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_device()) + .unwrap(); + builder.build().unwrap() + } +} +impl HasSampleValues for RecoveryRoleWithFactorSourceIds { + /// Config MFA 1.1 + fn sample() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_device()) + .unwrap(); + + builder + .add_factor_source(FactorSourceID::sample_ledger()) + .unwrap(); + builder.build().unwrap() + } + + /// Config MFA 3.3 + fn sample_other() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_ledger_other()) + .unwrap(); + + builder.build().unwrap() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = ConfirmationRoleWithFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn get_all_factors() { + let sut = SUT::sample(); + let factors = sut.all_factors(); + assert_eq!( + factors.len(), + sut.get_override_factors().len() + + sut.get_threshold_factors().len() + ); + } + + #[test] + fn assert_json() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "password", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" + } + } + ] + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/mod.rs new file mode 100644 index 000000000..206017bd7 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/mod.rs @@ -0,0 +1,9 @@ +mod confirmation_role_with_factor_source_ids; +mod primary_role_with_factor_source_ids; +mod recovery_role_with_factor_source_ids; +mod roles_with_factor_ids; + +pub use confirmation_role_with_factor_source_ids::*; +pub use primary_role_with_factor_source_ids::*; +pub use recovery_role_with_factor_source_ids::*; +pub use roles_with_factor_ids::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs new file mode 100644 index 000000000..1589cb921 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs @@ -0,0 +1,106 @@ +use crate::prelude::*; + +pub type PrimaryRoleWithFactorSourceIds = + RoleWithFactorSourceIds<{ ROLE_PRIMARY }>; + +impl PrimaryRoleWithFactorSourceIds { + /// Config MFA 1.1 + pub fn sample_primary() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source_to_threshold(FactorSourceID::sample_device()) + .unwrap(); + + builder + .add_factor_source_to_threshold(FactorSourceID::sample_ledger()) + .unwrap(); + builder.set_threshold(2).unwrap(); + builder.build().unwrap() + } +} + +impl HasSampleValues for PrimaryRoleWithFactorSourceIds { + fn sample() -> Self { + Self::sample_primary() + } + + fn sample_other() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source_to_threshold(FactorSourceID::sample_device()) + .unwrap(); + + builder + .add_factor_source_to_threshold(FactorSourceID::sample_ledger()) + .unwrap(); + builder.set_threshold(1).unwrap(); + builder.build().unwrap() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleWithFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn get_all_factors() { + let sut = SUT::sample_primary(); + let factors = sut.all_factors(); + assert_eq!( + factors.len(), + sut.get_override_factors().len() + + sut.get_threshold_factors().len() + ); + } + + #[test] + fn get_threshold() { + let sut = SUT::sample_primary(); + assert_eq!(sut.get_threshold(), 2); + } + + #[test] + fn assert_json_sample_primary() { + let sut = SUT::sample_primary(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 2, + "thresholdFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ], + "overrideFactors": [] + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs new file mode 100644 index 000000000..26edb425d --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs @@ -0,0 +1,65 @@ +use crate::prelude::*; + +pub type RecoveryRoleWithFactorSourceIds = + RoleWithFactorSourceIds<{ ROLE_RECOVERY }>; + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = RecoveryRoleWithFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn get_all_factors() { + let sut = SUT::sample(); + let factors = sut.all_factors(); + assert_eq!( + factors.len(), + sut.get_override_factors().len() + + sut.get_threshold_factors().len() + ); + } + + #[test] + fn assert_json() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ] + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs new file mode 100644 index 000000000..fd2d723da --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs @@ -0,0 +1,4 @@ +use crate::prelude::*; + +pub type RoleWithFactorSourceIds = + AbstractBuiltRoleWithFactor; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/mod.rs new file mode 100644 index 000000000..4343dd10f --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/mod.rs @@ -0,0 +1,3 @@ +mod role_template; + +pub use role_template::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs new file mode 100644 index 000000000..4cd2f1105 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs @@ -0,0 +1,105 @@ +use crate::prelude::*; + +/// A "template" FactorSourceID/FactorSource to be used in a RoleTemplate is +/// FactorSourceKind with some placeholder ID, to distinguish between two different +/// FactorSourceIDs of some kind, e.g. `FactorSourceID::sample()` and `FactorSourceID::sample_other()`. +/// but exactly which FactorSourceID values are not yet known, since this is a template. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FactorSourceTemplate { + /// The kind of FactorSource, e.g. Device, LedgerHQHardwareWallet, Password, etc. + pub kind: FactorSourceKind, + + /// Some placeholder ID to distinguish between two different FactorSourceIDs + /// to be concretely defined later. + pub id: u8, +} + +pub(crate) type RoleTemplate = + AbstractBuiltRoleWithFactor; + +pub(crate) type PrimaryRoleTemplate = RoleTemplate<{ ROLE_PRIMARY }>; +pub(crate) type RecoveryRoleTemplate = RoleTemplate<{ ROLE_RECOVERY }>; +pub(crate) type ConfirmationRoleTemplate = RoleTemplate<{ ROLE_CONFIRMATION }>; + +impl PrimaryRoleTemplate { + pub(crate) fn new( + threshold_factors: impl IntoIterator, + ) -> Self { + let threshold_factors = threshold_factors.into_iter().collect_vec(); + Self::with_factors(threshold_factors.len() as u8, threshold_factors, []) + } +} + +impl RecoveryRoleTemplate { + pub(crate) fn new( + override_factors: impl IntoIterator, + ) -> Self { + Self::with_factors(0, [], override_factors) + } +} + +impl ConfirmationRoleTemplate { + pub(crate) fn new( + override_factors: impl IntoIterator, + ) -> Self { + Self::with_factors(0, [], override_factors) + } +} + +impl FactorSourceTemplate { + pub fn new(kind: FactorSourceKind, id: u8) -> Self { + Self { kind, id } + } + + pub fn device() -> Self { + Self::new(FactorSourceKind::Device, 0) + } + + fn ledger_id(id: u8) -> Self { + Self::new(FactorSourceKind::LedgerHQHardwareWallet, id) + } + pub fn ledger() -> Self { + Self::ledger_id(0) + } + + pub fn ledger_other() -> Self { + Self::ledger_id(1) + } + + fn password_id(id: u8) -> Self { + Self::new(FactorSourceKind::Password, id) + } + pub fn password() -> Self { + Self::password_id(0) + } + pub fn password_other() -> Self { + Self::password_id(1) + } + + /// Radix Wallet (UI) calls this "Passphrase" + pub fn off_device_mnemonic() -> Self { + Self::new(FactorSourceKind::OffDeviceMnemonic, 0) + } + + fn trusted_contact_id(id: u8) -> Self { + Self::new(FactorSourceKind::TrustedContact, id) + } + + pub fn trusted_contact() -> Self { + Self::trusted_contact_id(0) + } + + pub fn trusted_contact_other() -> Self { + Self::trusted_contact_id(1) + } + + pub fn security_questions() -> Self { + Self::new(FactorSourceKind::SecurityQuestions, 0) + } +} + +impl IsMaybeKeySpaceAware for FactorSourceTemplate { + fn maybe_key_space(&self) -> Option { + None + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/confirmation_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs similarity index 53% rename from crates/sargon/src/profile/mfa/security_structures/factor_source_level/confirmation_role_with_factor_sources.rs rename to crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs index 433e99299..f69ea6a12 100644 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/confirmation_role_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs @@ -1,28 +1,19 @@ use crate::prelude::*; +pub(crate) type ConfirmationRoleWithFactorSources = + RoleWithFactorSources<{ ROLE_CONFIRMATION }>; + impl HasSampleValues for ConfirmationRoleWithFactorSources { fn sample() -> Self { - Self::new( - [], - 0, - [ - FactorSource::sample_security_questions(), - FactorSource::sample_ledger(), - ], - ) - .unwrap() + let ids = ConfirmationRoleWithFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() } fn sample_other() -> Self { - Self::new( - [], - 0, - [ - FactorSource::sample_security_questions_other(), - FactorSource::sample_ledger_other(), - ], - ) - .unwrap() + let ids = ConfirmationRoleWithFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() } } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs new file mode 100644 index 000000000..b53a5e147 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs @@ -0,0 +1,6 @@ +mod confirmation_role_with_factor_sources; +mod primary_role_with_factor_sources; +mod recovery_role_with_factor_sources; +mod roles_with_factor_sources; + +pub(crate) use roles_with_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs new file mode 100644 index 000000000..5801e689e --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs @@ -0,0 +1,37 @@ +use crate::prelude::*; + +pub(crate) type PrimaryRoleWithFactorSources = + RoleWithFactorSources<{ ROLE_PRIMARY }>; + +impl HasSampleValues for PrimaryRoleWithFactorSources { + fn sample() -> Self { + let ids = PrimaryRoleWithFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } + + fn sample_other() -> Self { + let ids = PrimaryRoleWithFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleWithFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/recovery_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs similarity index 54% rename from crates/sargon/src/profile/mfa/security_structures/factor_source_level/recovery_role_with_factor_sources.rs rename to crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs index 4a5aaa919..6482c4dc2 100644 --- a/crates/sargon/src/profile/mfa/security_structures/factor_source_level/recovery_role_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs @@ -1,24 +1,19 @@ use crate::prelude::*; +pub(crate) type RecoveryRoleWithFactorSources = + RoleWithFactorSources<{ ROLE_RECOVERY }>; + impl HasSampleValues for RecoveryRoleWithFactorSources { fn sample() -> Self { - Self::new( - [ - FactorSource::sample_arculus(), - FactorSource::sample_arculus_other(), - ], - 2, - [FactorSource::sample_ledger()], - ) - .unwrap() + let ids = RecoveryRoleWithFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() } + fn sample_other() -> Self { - Self::new( - [FactorSource::sample_arculus_other()], - 1, - [FactorSource::sample_ledger_other()], - ) - .unwrap() + let ids = RecoveryRoleWithFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() } } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs new file mode 100644 index 000000000..a34d8cd00 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +pub(crate) type RoleWithFactorSources = + AbstractBuiltRoleWithFactor; + +impl RoleWithFactorSources { + pub fn new( + role_with_factor_source_ids: RoleWithFactorSourceIds, + factor_sources: &FactorSources, + ) -> Result { + let lookup_f = + |id: &FactorSourceID| -> Result { + factor_sources + .get_id(id) + .ok_or(CommonError::FactorSourceDiscrepancy) + .cloned() + }; + + let lookup = |ids: &Vec| -> Result, CommonError> { + ids.iter() + .map(lookup_f) + .collect::, CommonError>>() + }; + + let threshold_factors = + lookup(role_with_factor_source_ids.get_threshold_factors())?; + let override_factors = + lookup(role_with_factor_source_ids.get_override_factors())?; + + Ok(Self::with_factors( + role_with_factor_source_ids.get_threshold(), + threshold_factors, + override_factors, + )) + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs new file mode 100644 index 000000000..4e39fbee0 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs @@ -0,0 +1,9 @@ +mod factor_instance_level; +mod factor_source_id_level; +mod factor_source_kind_level; +mod factor_source_level; + +pub use factor_instance_level::*; +pub use factor_source_id_level::*; +pub use factor_source_kind_level::*; +pub(crate) use factor_source_level::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs new file mode 100644 index 000000000..c99cb3972 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs @@ -0,0 +1,7 @@ +mod abstract_role_builder_or_built; +mod builder; +mod factor_levels; + +pub(crate) use abstract_role_builder_or_built::*; +pub use builder::*; +pub use factor_levels::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structure_id.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_id.rs similarity index 100% rename from crates/sargon/src/profile/mfa/security_structures/factor_source_id_level/security_structure_id.rs rename to crates/sargon/src/profile/mfa/security_structures/security_structure_id.rs diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs new file mode 100644 index 000000000..06d9c15f6 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs @@ -0,0 +1,47 @@ +use crate::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AbstractSecurityStructure { + /// Metadata of this Security Structure, such as globally unique and + /// stable identifier, creation date and user chosen label (name). + pub metadata: SecurityStructureMetadata, + + /// The structure of factors to use for certain roles, Primary, Recovery + /// and Confirmation role. + pub matrix_of_factors: AbstractMatrixBuilt, +} + +impl Identifiable for AbstractSecurityStructure { + type ID = ::ID; + + fn id(&self) -> Self::ID { + self.metadata.id() + } +} + +impl AbstractSecurityStructure { + pub fn all_factors(&self) -> HashSet<&F> { + self.matrix_of_factors.all_factors() + } +} + +impl AbstractSecurityStructure { + pub fn with_metadata( + metadata: SecurityStructureMetadata, + matrix_of_factors: AbstractMatrixBuilt, + ) -> Self { + Self { + metadata, + matrix_of_factors, + } + } + + pub fn new( + display_name: DisplayName, + matrix_of_factors: AbstractMatrixBuilt, + ) -> Self { + let metadata = SecurityStructureMetadata::new(display_name); + Self::with_metadata(metadata, matrix_of_factors) + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/mod.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/mod.rs new file mode 100644 index 000000000..9e2b04e3b --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/mod.rs @@ -0,0 +1,7 @@ +mod abstract_security_structure_of_factors; +mod security_structure_of_factor_source_ids; +mod security_structure_of_factor_sources; + +pub(crate) use abstract_security_structure_of_factors::*; +pub use security_structure_of_factor_source_ids::*; +pub use security_structure_of_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs new file mode 100644 index 000000000..d66b89f9c --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs @@ -0,0 +1,218 @@ +use crate::prelude::*; + +pub type SecurityStructureOfFactorSourceIds = + AbstractSecurityStructure; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[serde(rename_all = "camelCase")] +pub struct SecurityStructureOfFactorInstances { + /// The ID of the `SecurityStructureOfFactorSourceIDs` in + /// `profile.app_preferences.security.security_structures_of_factor_source_ids` + /// which was used to derive the factor instances in this structure. Or rather: + /// The id of `SecurityStructureOfFactorSources`. + pub security_structure_id: SecurityStructureID, + + /// The structure of factors to use for certain roles, Primary, Recovery + /// and Confirmation role. + pub matrix_of_factors: MatrixOfFactorInstances, +} + +impl SecurityStructureOfFactorInstances { + pub fn new( + security_structure_id: SecurityStructureID, + matrix_of_factors: MatrixOfFactorInstances, + ) -> Self { + Self { + security_structure_id, + matrix_of_factors, + } + } +} + +impl Identifiable for SecurityStructureOfFactorInstances { + type ID = ::ID; + + fn id(&self) -> Self::ID { + self.security_structure_id + } +} + +impl HasSampleValues for SecurityStructureOfFactorInstances { + fn sample() -> Self { + Self { + security_structure_id: SecurityStructureID::sample(), + matrix_of_factors: MatrixOfFactorInstances::sample(), + } + } + + fn sample_other() -> Self { + Self { + security_structure_id: SecurityStructureID::sample_other(), + matrix_of_factors: MatrixOfFactorInstances::sample_other(), + } + } +} + +impl HasSampleValues for SecurityStructureOfFactorSourceIds { + fn sample() -> Self { + let metadata = SecurityStructureMetadata::sample(); + Self::with_metadata(metadata, MatrixOfFactorSourceIds::sample()) + } + + fn sample_other() -> Self { + let metadata = SecurityStructureMetadata::sample_other(); + Self::with_metadata(metadata, MatrixOfFactorSourceIds::sample_other()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[allow(clippy::upper_case_acronyms)] + type SUT = SecurityStructureOfFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn assert_json_sample() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "metadata": { + "id": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "displayName": "Spending Account", + "createdOn": "2023-09-11T16:05:56.000Z", + "lastUpdatedOn": "2023-09-11T16:05:56.000Z" + }, + "matrixOfFactors": { + "primaryRole": { + "threshold": 2, + "thresholdFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ], + "overrideFactors": [] + }, + "recoveryRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ] + }, + "confirmationRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "password", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" + } + } + ] + }, + "numberOfDaysUntilAutoConfirm": 14 + } + } + "#, + ); + } + + #[test] + fn assert_json_sample_other() { + let sut = SUT::sample_other(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "metadata": { + "id": "dededede-dede-dede-dede-dededededede", + "displayName": "Savings Account", + "createdOn": "2023-12-24T17:13:56.123Z", + "lastUpdatedOn": "2023-12-24T17:13:56.123Z" + }, + "matrixOfFactors": { + "primaryRole": { + "threshold": 1, + "thresholdFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + } + ], + "overrideFactors": [] + }, + "recoveryRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ] + }, + "confirmationRole": { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "52ef052a0642a94279b296d6b3b17dedc035a7ae37b76c1d60f11f2725100077" + } + } + ] + }, + "numberOfDaysUntilAutoConfirm": 14 + } + } + "#, + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs new file mode 100644 index 000000000..4c67e0d8e --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs @@ -0,0 +1,92 @@ +use crate::prelude::*; + +pub type SecurityStructureOfFactorSources = + AbstractSecurityStructure; + +impl HasSampleValues for SecurityStructureOfFactorSources { + fn sample() -> Self { + let metadata = SecurityStructureMetadata::sample(); + Self::with_metadata(metadata, MatrixOfFactorSources::sample()) + } + + fn sample_other() -> Self { + let metadata = SecurityStructureMetadata::sample_other(); + Self::with_metadata(metadata, MatrixOfFactorSources::sample_other()) + } +} + +pub type MatrixOfFactorSourceIDs = MatrixOfFactorSourceIds; + +impl TryFrom<(&MatrixOfFactorSourceIDs, &FactorSources)> + for MatrixOfFactorSources +{ + type Error = CommonError; + fn try_from( + value: (&MatrixOfFactorSourceIDs, &FactorSources), + ) -> Result { + Self::new(value.0.clone(), value.1) + } +} + +impl TryFrom<(&SecurityStructureOfFactorSourceIDs, &FactorSources)> + for SecurityStructureOfFactorSources +{ + type Error = CommonError; + fn try_from( + value: (&SecurityStructureOfFactorSourceIDs, &FactorSources), + ) -> Result { + let (id_level, factor_sources) = value; + let matrix_of_factors = MatrixOfFactorSources::try_from(( + &id_level.matrix_of_factors, + factor_sources, + ))?; + Ok(Self { + metadata: id_level.metadata.clone(), + matrix_of_factors, + }) + } +} + +impl From + for SecurityStructureOfFactorSourceIDs +{ + fn from(value: SecurityStructureOfFactorSources) -> Self { + Self { + metadata: value.metadata, + matrix_of_factors: value.matrix_of_factors.into(), + } + } +} + +impl From for MatrixOfFactorSourceIDs { + fn from(value: MatrixOfFactorSources) -> Self { + todo!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[allow(clippy::upper_case_acronyms)] + type SUT = SecurityStructureOfFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn test_into_id_level_and_back() { + let factor_sources = FactorSources::sample_values_all(); + let sut = SUT::sample(); + let id_level = SecurityStructureOfFactorSourceIDs::from(sut.clone()); + let detailed = SUT::try_from((&id_level, &factor_sources)).unwrap(); + assert_eq!(detailed, sut); + } +} diff --git a/crates/sargon/src/profile/v100/app_preferences/security.rs b/crates/sargon/src/profile/v100/app_preferences/security.rs index 6a9f5c156..cc1bc40c9 100644 --- a/crates/sargon/src/profile/v100/app_preferences/security.rs +++ b/crates/sargon/src/profile/v100/app_preferences/security.rs @@ -1,5 +1,26 @@ use crate::prelude::*; +pub type SecurityStructureOfFactorSourceIDs = + SecurityStructureOfFactorSourceIds; + +decl_identified_vec_of!( + /// A collection of [`SecurityStructureOfFactorSourceIDs`] + SecurityStructuresOfFactorSourceIDs, + SecurityStructureOfFactorSourceIDs +); + +impl HasSampleValues for SecurityStructuresOfFactorSourceIDs { + fn sample() -> Self { + Self::from_iter([ + SecurityStructureOfFactorSourceIDs::sample(), + SecurityStructureOfFactorSourceIDs::sample_other(), + ]) + } + fn sample_other() -> Self { + Self::from_iter([SecurityStructureOfFactorSourceIDs::sample_other()]) + } +} + /// Controls e.g. if Profile Snapshot gets synced to iCloud or not, and whether /// developer mode is enabled or not. In future (MFA) we will also save a list of /// MFA security structure configurations. diff --git a/crates/sargon/src/signing/collector/signatures_collector.rs b/crates/sargon/src/signing/collector/signatures_collector.rs index 88dd49125..f71a36f93 100644 --- a/crates/sargon/src/signing/collector/signatures_collector.rs +++ b/crates/sargon/src/signing/collector/signatures_collector.rs @@ -1489,16 +1489,13 @@ mod tests { .transaction_signing .factor_instance()]) } - EntitySecurityState::Securified { value } => { - let matrix = value - .security_structure - .matrix_of_factors - .clone(); - let mut set = IndexSet::new(); - set.extend(matrix.primary_role.threshold_factors); - set.extend(matrix.primary_role.override_factors); - set - } + EntitySecurityState::Securified { value } => value + .security_structure + .matrix_of_factors + .all_factors() + .into_iter() + .cloned() + .collect::>(), } } } diff --git a/crates/sargon/src/signing/petition_types/general_role_with_hd_factor_instance.rs b/crates/sargon/src/signing/petition_types/general_role_with_hd_factor_instance.rs deleted file mode 100644 index f1af40b14..000000000 --- a/crates/sargon/src/signing/petition_types/general_role_with_hd_factor_instance.rs +++ /dev/null @@ -1,225 +0,0 @@ -use crate::prelude::*; - -decl_role_runtime_kind_with_factors!( - /// A general depiction of each of the roles in a `MatrixOfFactorInstances`. - /// `SignaturesCollector` can work on any `RoleKind` when dealing with a securified entity. - General, - HierarchicalDeterministicFactorInstance -); - -impl HasRoleKindObjectSafe - for GeneralRoleWithHierarchicalDeterministicFactorInstances -{ - fn get_role_kind(&self) -> RoleKind { - self.role - } -} - -impl TryFrom<(MatrixOfFactorInstances, RoleKind)> - for GeneralRoleWithHierarchicalDeterministicFactorInstances -{ - type Error = CommonError; - - fn try_from( - (matrix, role): (MatrixOfFactorInstances, RoleKind), - ) -> Result { - let (threshold_factors, threshold, override_factors) = match role { - RoleKind::Primary => ( - matrix.primary_role.threshold_factors, - matrix.primary_role.threshold, - matrix.primary_role.override_factors, - ), - RoleKind::Recovery => ( - matrix.recovery_role.threshold_factors, - matrix.recovery_role.threshold, - matrix.recovery_role.override_factors, - ), - RoleKind::Confirmation => ( - matrix.confirmation_role.threshold_factors, - matrix.confirmation_role.threshold, - matrix.confirmation_role.override_factors, - ), - }; - - GeneralRoleWithHierarchicalDeterministicFactorInstances::with_factors_and_role( - role, - threshold_factors - .iter() - .map(|f| HierarchicalDeterministicFactorInstance::try_from_factor_instance(f.clone())) - .collect::>>()?, - threshold, - override_factors - .iter() - .map(|f| HierarchicalDeterministicFactorInstance::try_from_factor_instance(f.clone())) - .collect::>>()?, - ) - } -} - -impl GeneralRoleWithHierarchicalDeterministicFactorInstances { - pub fn single_override( - role: RoleKind, - factor: HierarchicalDeterministicFactorInstance, - ) -> Self { - assert!(factor.is_securified(), "non securified factor"); - Self::with_factors_and_role(role, [], 0, [factor]) - .expect("Zero threshold with zero threshold factors and one override should not fail.") - } - - pub fn single_threshold( - role: RoleKind, - factor: HierarchicalDeterministicFactorInstance, - ) -> Self { - assert!(factor.is_securified(), "non securified factor"); - Self::with_factors_and_role(role, [factor], 1, []).expect( - "Single threshold with one threshold factor should not fail.", - ) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = GeneralRoleWithHierarchicalDeterministicFactorInstances; - - #[test] - fn test_from_primary_role() { - assert_eq!( - SUT::try_from( - (matrix(), RoleKind::Primary) - ).unwrap(), - SUT::with_factors_and_role( - RoleKind::Primary, - [ - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(0) - ], - 1, - [] - ).unwrap() - ) - } - - #[test] - fn test_get_role() { - let test = |role: RoleKind| { - let sut = SUT::single_override( - role, - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(0) - ); - assert_eq!(sut.role, role); - }; - test(RoleKind::Primary); - test(RoleKind::Confirmation); - test(RoleKind::Recovery); - } - - #[test] - fn test_from_recovery_role() { - let r = recovery_role(); - assert_eq!( - SUT::try_from( - (matrix(), RoleKind::Recovery) - ).unwrap(), - SUT::with_factors_and_role( - RoleKind::Recovery, - r.threshold_factors - .clone() - .into_iter() - .map(|f: FactorInstance| { - HierarchicalDeterministicFactorInstance::try_from_factor_instance(f) - .unwrap() - }) - .collect_vec(), - 1, - [] - ).unwrap() - ) - } - - #[test] - fn test_from_confirmation_role() { - let r = confirmation_role(); - assert_eq!( - SUT::try_from((matrix(), RoleKind::Confirmation)).unwrap(), - SUT::with_factors_and_role( - RoleKind::Confirmation, - r.threshold_factors - .clone() - .into_iter() - .map(|f: FactorInstance| { - HierarchicalDeterministicFactorInstance::try_from_factor_instance(f) - .unwrap() - }) - .collect_vec(), - r.threshold, - r.override_factors - .clone() - .into_iter() - .map(|f: FactorInstance| { - HierarchicalDeterministicFactorInstance::try_from_factor_instance(f) - .unwrap() - }) - .collect_vec() - ) - .unwrap() - ) - } - - #[test] - fn test_from_matrix_containing_physical_badge() { - let matrix = MatrixOfFactorInstances::new( - PrimaryRoleWithFactorInstances::new( - [FactorInstance::sample_other()], - 1, - [], - ) - .unwrap(), - recovery_role(), - confirmation_role(), - ) - .unwrap(); - - assert_eq!( - SUT::try_from((matrix, RoleKind::Primary)), - Err(CommonError::BadgeIsNotVirtualHierarchicalDeterministic) - ); - } - - fn matrix() -> MatrixOfFactorInstances { - MatrixOfFactorInstances::new( - primary_role(), - recovery_role(), - confirmation_role(), - ) - .unwrap() - } - - fn primary_role() -> PrimaryRoleWithFactorInstances { - PrimaryRoleWithFactorInstances::new([HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(0).into()], 1, []) - .unwrap() - } - - fn recovery_role() -> RecoveryRoleWithFactorInstances { - RecoveryRoleWithFactorInstances::new( - [ - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(1).into() - ], - 1, - [], - ) - .unwrap() - } - - fn confirmation_role() -> ConfirmationRoleWithFactorInstances { - ConfirmationRoleWithFactorInstances::new( - [ - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_0_securified_at_index(2).into() - ], - 1, - [], - ) - .unwrap() - } -} diff --git a/crates/sargon/src/signing/petition_types/mod.rs b/crates/sargon/src/signing/petition_types/mod.rs index a7f2827d3..406165902 100644 --- a/crates/sargon/src/signing/petition_types/mod.rs +++ b/crates/sargon/src/signing/petition_types/mod.rs @@ -1,5 +1,4 @@ mod factor_list_kind; -mod general_role_with_hd_factor_instance; mod petition_for_entity; mod petition_for_factors_types; mod petition_for_transaction; @@ -13,6 +12,5 @@ pub(crate) use petition_for_transaction::*; pub(crate) use petition_status::*; pub(crate) use petitions::*; -pub use general_role_with_hd_factor_instance::*; pub use petition_for_factors_types::*; pub use role_kind::*; diff --git a/crates/sargon/src/signing/petition_types/petition_for_entity.rs b/crates/sargon/src/signing/petition_types/petition_for_entity.rs index 75bc5c70c..9039c2bc0 100644 --- a/crates/sargon/src/signing/petition_types/petition_for_entity.rs +++ b/crates/sargon/src/signing/petition_types/petition_for_entity.rs @@ -79,11 +79,11 @@ impl PetitionForEntity { payload_id, entity, PetitionForFactors::new_threshold( - role_with_factor_instances.threshold_factors, - role_with_factor_instances.threshold as i8, + role_with_factor_instances.get_threshold_factors(), + role_with_factor_instances.get_threshold() as i8, ), PetitionForFactors::new_override( - role_with_factor_instances.override_factors, + role_with_factor_instances.get_override_factors(), ), ) } diff --git a/crates/sargon/src/types/samples/account_samples.rs b/crates/sargon/src/types/samples/account_samples.rs index 64c1213db..0f8b19808 100644 --- a/crates/sargon/src/types/samples/account_samples.rs +++ b/crates/sargon/src/types/samples/account_samples.rs @@ -156,6 +156,7 @@ impl Account { veci: HierarchicalDeterministicFactorInstance, make_role: impl Fn() -> GeneralRoleWithHierarchicalDeterministicFactorInstances, ) -> Self { + /* let role = make_role(); let threshold_factors = role @@ -191,6 +192,7 @@ impl Account { .unwrap(), ) .unwrap(); + let network_id = NetworkID::Mainnet; let address = AccountAddress::new(veci.public_key(), NetworkID::Mainnet); @@ -212,6 +214,8 @@ impl Account { flags: Default::default(), on_ledger_settings: Default::default(), } + */ + unimplemented!("TODO MFA-Role Rules"); } pub fn sample_at(index: usize) -> Self { diff --git a/crates/sargon/src/types/samples/persona_samples.rs b/crates/sargon/src/types/samples/persona_samples.rs index 15061d72a..1586dbaad 100644 --- a/crates/sargon/src/types/samples/persona_samples.rs +++ b/crates/sargon/src/types/samples/persona_samples.rs @@ -121,6 +121,7 @@ impl Persona { veci: HierarchicalDeterministicFactorInstance, make_role: impl Fn() -> GeneralRoleWithHierarchicalDeterministicFactorInstances, ) -> Self { + /* let role = make_role(); let threshold_factors = role @@ -175,6 +176,8 @@ impl Persona { flags: Default::default(), persona_data: Default::default(), } + */ + unimplemented!("migrate MFA-Role Rules") } pub fn sample_at(index: usize) -> Self { From 56419c303eec267e7dd7400edfa950b7341d38a6 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 30 Nov 2024 17:45:01 +0100 Subject: [PATCH 02/70] fix most tests --- .../factor_instances_provider_unit_tests.rs | 768 +++++++++--------- ...curify_entity_factor_instances_provider.rs | 266 +++--- .../matrices/builder/matrix_template.rs | 13 +- .../matrices/matrix_of_factor_instances.rs | 116 ++- .../matrices/matrix_of_factor_source_ids.rs | 35 +- .../factor_levels/factor_source_level/mod.rs | 3 + .../security_structure_of_factor_sources.rs | 25 +- .../profile/v100/app_preferences/security.rs | 2 +- .../src/types/samples/account_samples.rs | 50 +- .../src/types/samples/persona_samples.rs | 50 +- 10 files changed, 665 insertions(+), 663 deletions(-) diff --git a/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs b/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs index a4796160d..35fa88036 100644 --- a/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs +++ b/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs @@ -462,28 +462,28 @@ async fn cache_is_unchanged_in_case_of_failure() { assert_eq!(all_accounts.len(), 3 * n); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - let shield_0 = SecurityStructureOfFactorSources::sample(); + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let all_accounts = os .profile() @@ -611,29 +611,32 @@ async fn test_assert_factor_instances_invalid() { .unwrap(); let bdfs = FactorSource::from(os.bdfs().unwrap()); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - let shield_0 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let (security_structure_of_fis, _, _) = os.make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome(IndexSet::from_iter([alice.address()]), shield_0.clone()).await.unwrap(); let security_structure_of_fi = @@ -951,31 +954,28 @@ async fn test_securified_accounts() { os.add_factor_source(arculus.clone()).await.unwrap(); os.add_factor_source(password.clone()).await.unwrap(); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - let shield_0 = SecurityStructureOfFactorSources::sample(); + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 2, + [bdfs.clone(), ledger.clone(), arculus.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -990,7 +990,7 @@ async fn test_securified_accounts() { "should have used cache" ); - // dont forget to consume! + // Don't forget to consume! instances_in_cache_consumer.consume().await.unwrap(); let alice_sec = security_structures_of_fis.get(&alice.address()).unwrap(); @@ -1089,22 +1089,28 @@ async fn test_securified_accounts() { "First account created with ledger, should have index 0, even though this ledger was used in the shield, since we are using two different KeySpaces for Securified and Unsecurified accounts." ); - // let matrix_1 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([password.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([password.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([password.clone()]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_1 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), - // 14, - // matrix_1, - // ); - let shield_1 = SecurityStructureOfFactorSources::sample_other(); + let matrix_1 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [password.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [password.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [password.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_1 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_1); let (security_structures_of_fis, instances_in_cache_consumer, _) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1114,13 +1120,13 @@ async fn test_securified_accounts() { .await .unwrap(); - // dont forget to consume! + // Don't forget to consume! instances_in_cache_consumer.consume().await.unwrap(); let carol_sec = security_structures_of_fis.get(&carol.address()).unwrap(); let carol_matrix = carol_sec.matrix_of_factors.clone(); - assert_eq!(carol_matrix.primary_role.get_override_factors().len(), 1); + assert_eq!(carol_matrix.primary_role.get_threshold_factors().len(), 1); assert_eq!( carol_matrix @@ -1216,29 +1222,31 @@ async fn securify_accounts_when_cache_is_half_full_single_factor_source() { .await .unwrap(); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - let shield_0 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let profile = os.profile().unwrap(); let all_accounts = profile .accounts_on_all_networks_including_hidden() @@ -1362,33 +1370,31 @@ async fn securify_accounts_when_cache_is_half_full_multiple_factor_sources() { assert_eq!(derivation_outcome.debug_was_derived.len(), 3 * n); // `n` missing + CACHE filling 2*n more. - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - - let shield_0 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 2, + [bdfs.clone(), ledger.clone(), arculus.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let all_accounts = os .profile() .unwrap() @@ -1602,30 +1608,31 @@ async fn securify_personas_when_cache_is_half_full_single_factor_source() { let (_, _) = os.batch_create_many_personas_with_bdfs_with_derivation_outcome_then_save_once(3 * n as u16, NetworkID::Mainnet, "Persona".to_owned()).await.unwrap(); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only([bdfs.clone()], 1) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone()], - // 1, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - - let shield_0 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let all_personas = os .profile() .unwrap() @@ -1735,21 +1742,31 @@ async fn create_single_account() { "should have used cache" ); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([bdfs.clone()]).unwrap(), - // RecoveryRoleWithFactorSources::override_only([bdfs.clone()]).unwrap(), - // ConfirmationRoleWithFactorSources::override_only([bdfs.clone()]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ) + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; - let shield_0 = SecurityStructureOfFactorSources::sample(); + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1759,7 +1776,7 @@ async fn create_single_account() { .await .unwrap(); - // dont forget to consume! + // Don't forget to consume! instances_in_cache_consumer.consume().await.unwrap(); assert!( @@ -1818,32 +1835,28 @@ async fn securified_personas() { os.add_factor_source(arculus.clone()).await.unwrap(); os.add_factor_source(password.clone()).await.unwrap(); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [bdfs.clone(), ledger.clone(), arculus.clone()], - // 2, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - - let shield_0 = SecurityStructureOfFactorSources::sample(); + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 2, + [bdfs.clone(), ledger.clone(), arculus.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1858,7 +1871,7 @@ async fn securified_personas() { "should have used cache" ); - // dont forget to consume! + // Don't forget to consume! instances_in_cache_consumer.consume().await.unwrap(); let batman_sec = security_structures_of_fis.get(&batman.address()).unwrap(); @@ -1958,23 +1971,31 @@ async fn securified_personas() { "First persona created with ledger, should have index 0, even though this ledger was used in the shield, since we are using two different KeySpaces for Securified and Unsecurified personas." ); - // let matrix_1 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([password.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([password.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([password.clone()]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_1 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), - // 14, - // matrix_1, - // ); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_1 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [password.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [password.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [password.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; - let shield_1 = SecurityStructureOfFactorSources::sample_other(); + let shield_1 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_1); let (security_structures_of_fis, instances_in_cache_consumer, derivation_outcome) = os .make_security_structure_of_factor_instances_for_entities_without_consuming_cache_with_derivation_outcome( @@ -1984,7 +2005,7 @@ async fn securified_personas() { .await .unwrap(); - // dont forget to consume! + // Don't forget to consume! instances_in_cache_consumer.consume().await.unwrap(); assert!( @@ -1995,7 +2016,7 @@ async fn securified_personas() { let hyde_sec = security_structures_of_fis.get(&hyde.address()).unwrap(); let hyde_matrix = hyde_sec.matrix_of_factors.clone(); - assert_eq!(hyde_matrix.primary_role.get_override_factors().len(), 1); + assert_eq!(hyde_matrix.primary_role.get_threshold_factors().len(), 1); assert_eq!( hyde_matrix @@ -2077,12 +2098,12 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { DerivationPreset::all().len() * CACHE_FILLING_QUANTITY ); - let fs_device = FactorSource::from(os.bdfs().unwrap()); - let fs_arculus = FactorSource::sample_arculus(); - let fs_ledger = FactorSource::sample_ledger(); + let bdfs = FactorSource::from(os.bdfs().unwrap()); + let arculus = FactorSource::sample_arculus(); + let ledger = FactorSource::sample_ledger(); - os.add_factor_source(fs_arculus.clone()).await.unwrap(); - os.add_factor_source(fs_ledger.clone()).await.unwrap(); + os.add_factor_source(arculus.clone()).await.unwrap(); + os.add_factor_source(ledger.clone()).await.unwrap(); let factor_source_count = os.profile().unwrap().factor_sources.len(); @@ -2114,24 +2135,31 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { .into_iter() .collect_vec(); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([fs_device.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([fs_device.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([fs_device.clone()]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - - let shield_0 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let (_, derivation_outcome) = os .__OFFLINE_ONLY_securify_accounts( unnamed_accounts @@ -2163,7 +2191,7 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { let next_index_veci = next_index_profile_assigner .next( - fs_device.id_from_hash(), + bdfs.id_from_hash(), DerivationPreset::AccountVeci .index_agnostic_path_on_network(network), ) @@ -2179,7 +2207,7 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { let next_index_mfa = next_index_profile_assigner .next( - fs_device.id_from_hash(), + bdfs.id_from_hash(), DerivationPreset::AccountMfa .index_agnostic_path_on_network(network), ) @@ -2193,7 +2221,7 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { let (alice, derivation_outcome) = os .create_and_save_new_account_with_factor_with_derivation_outcome( - fs_device.clone(), + bdfs.clone(), network, "Alice", ) @@ -2267,18 +2295,18 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { #[actix_rt::test] async fn securified_accounts_asymmetric_indices() { - let (os, fs_device) = SargonOS::with_bdfs().await; + let (os, bdfs) = SargonOS::with_bdfs().await; let cache = os.cache_snapshot().await; assert_eq!( cache.total_number_of_factor_instances(), DerivationPreset::all().len() * CACHE_FILLING_QUANTITY ); - let fs_arculus = FactorSource::sample_arculus(); - let fs_ledger = FactorSource::sample_ledger(); + let arculus = FactorSource::sample_arculus(); + let ledger = FactorSource::sample_ledger(); - os.add_factor_source(fs_arculus.clone()).await.unwrap(); - os.add_factor_source(fs_ledger.clone()).await.unwrap(); + os.add_factor_source(arculus.clone()).await.unwrap(); + os.add_factor_source(ledger.clone()).await.unwrap(); let number_of_factor_sources = os.profile().unwrap().factor_sources.len(); assert_eq!(number_of_factor_sources, 3); @@ -2299,7 +2327,7 @@ async fn securified_accounts_asymmetric_indices() { // first create CACHE_FILLING_QUANTITY many "unnamed" accounts - let (_, derivation_outcome) = os.batch_create_many_accounts_with_factor_source_with_derivation_outcome_then_save_once(fs_device.clone(), CACHE_FILLING_QUANTITY as u16, network, "Acco".to_owned()).await.unwrap(); + let (_, derivation_outcome) = os.batch_create_many_accounts_with_factor_source_with_derivation_outcome_then_save_once(bdfs.clone(), CACHE_FILLING_QUANTITY as u16, network, "Acco".to_owned()).await.unwrap(); assert!(derivation_outcome.debug_was_derived.is_empty()); let unnamed_accounts = os @@ -2309,33 +2337,31 @@ async fn securified_accounts_asymmetric_indices() { .into_iter() .collect_vec(); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::threshold_factors_only( - // [fs_device.clone()], - // 1, - // ) - // .unwrap(), - // RecoveryRoleWithFactorSources::threshold_factors_only( - // [fs_device.clone()], - // 1, - // ) - // .unwrap(), - // ConfirmationRoleWithFactorSources::threshold_factors_only( - // [fs_device.clone()], - // 1, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_0 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 0").unwrap()), - // 14, - // matrix_0, - // ); - - let shield_0 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_0 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 1, + [bdfs.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + let shield_0 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_0); let (_, derivation_outcome) = os .__OFFLINE_ONLY_securify_accounts( unnamed_accounts @@ -2355,7 +2381,7 @@ async fn securified_accounts_asymmetric_indices() { let (alice, derivation_outcome) = os .create_and_save_new_account_with_factor_with_derivation_outcome( - fs_device.clone(), + bdfs.clone(), network, "Alice", ) @@ -2376,7 +2402,7 @@ async fn securified_accounts_asymmetric_indices() { let (bob, _) = os .create_and_save_new_account_with_factor_with_derivation_outcome( - fs_device.clone(), + bdfs.clone(), network, "Bob", ) @@ -2385,7 +2411,7 @@ async fn securified_accounts_asymmetric_indices() { let (carol, _) = os .create_and_save_new_account_with_factor_with_derivation_outcome( - fs_device.clone(), + bdfs.clone(), network, "Carol", ) @@ -2394,7 +2420,7 @@ async fn securified_accounts_asymmetric_indices() { let (diana, _) = os .create_and_save_new_account_with_factor_with_derivation_outcome( - fs_device.clone(), + bdfs.clone(), network, "Diana", ) @@ -2425,32 +2451,31 @@ async fn securified_accounts_asymmetric_indices() { 4 ); - // let matrix_1 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_arculus.clone(), - // ]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_arculus.clone(), - // ]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_arculus.clone(), - // ]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_1 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 1").unwrap()), - // 14, - // matrix_1, - // ); - - let shield_1 = SecurityStructureOfFactorSources::sample_other(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_1 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 2, + [bdfs.clone(), arculus.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), arculus.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), arculus.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_1 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_1); let (securified_alice, derivation_outcome) = os .__OFFLINE_ONLY_securify_account(alice.address(), &shield_1) @@ -2478,12 +2503,12 @@ async fn securified_accounts_asymmetric_indices() { .collect::>(), [ ( - fs_device.id_from_hash(), + bdfs.id_from_hash(), HDPathComponent::from_local_key_space(30, KeySpace::Securified) .unwrap() ), ( - fs_arculus.id_from_hash(), + arculus.id_from_hash(), HDPathComponent::from_local_key_space(0, KeySpace::Securified) .unwrap() ), @@ -2492,32 +2517,31 @@ async fn securified_accounts_asymmetric_indices() { .collect::>() ); - // let matrix_2 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_ledger.clone(), - // ]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_ledger.clone(), - // ]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_ledger.clone(), - // ]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_2 = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 2").unwrap()), - // 14, - // matrix_2, - // ); - - let shield_2 = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_2 = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 2, + [bdfs.clone(), ledger.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_2 = + SecurityStructureOfFactorSources::new(DisplayName::sample(), matrix_2); let (securified_bob, derivation_outcome) = os .__OFFLINE_ONLY_securify_account(bob.address(), &shield_2) @@ -2545,12 +2569,12 @@ async fn securified_accounts_asymmetric_indices() { .collect::>(), [ ( - fs_device.id_from_hash(), + bdfs.id_from_hash(), HDPathComponent::from_local_key_space(31, KeySpace::Securified) // Alice used 30 .unwrap() ), ( - fs_ledger.id_from_hash(), + ledger.id_from_hash(), HDPathComponent::from_local_key_space(0, KeySpace::Securified) .unwrap() ), @@ -2584,12 +2608,12 @@ async fn securified_accounts_asymmetric_indices() { .collect::>(), [ ( - fs_device.id_from_hash(), + bdfs.id_from_hash(), HDPathComponent::from_local_key_space(32, KeySpace::Securified) // Alice used 30, Bob used 31 .unwrap() ), ( - fs_arculus.id_from_hash(), + arculus.id_from_hash(), HDPathComponent::from_local_key_space(1, KeySpace::Securified) // Alice used 0 .unwrap() ), @@ -2601,35 +2625,33 @@ async fn securified_accounts_asymmetric_indices() { // CLEAR CACHE os.clear_cache().await; - // let matrix_3fa = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_arculus.clone(), - // fs_ledger.clone(), - // ]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_arculus.clone(), - // fs_ledger.clone(), - // ]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([ - // fs_device.clone(), - // fs_arculus.clone(), - // fs_ledger.clone(), - // ]) - // .unwrap(), - // ) - // .unwrap(); - - // let shield_3fa = SecurityStructureOfFactorSources::new( - // SecurityStructureMetadata::new(DisplayName::new("Shield 3fa").unwrap()), - // 14, - // matrix_3fa, - // ); - - let shield_3fa = SecurityStructureOfFactorSources::sample(); + // This is NOT a valid Matrix! But for the purpose of this test, it's fine. + // We are not testing valid matrices here... we are testing the factor + // instances provider... + let matrix_3fa = MatrixOfFactorSources { + built: PhantomData, + primary_role: PrimaryRoleWithFactorSources::with_factors( + 2, + [bdfs.clone(), ledger.clone(), arculus.clone()], + [], + ), + recovery_role: RecoveryRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + confirmation_role: ConfirmationRoleWithFactorSources::with_factors( + 0, + [], + [bdfs.clone(), ledger.clone(), arculus.clone()], + ), + number_of_days_until_auto_confirm: 1, + }; + + let shield_3fa = SecurityStructureOfFactorSources::new( + DisplayName::sample(), + matrix_3fa, + ); let (securified_diana, derivation_outcome) = os .__OFFLINE_ONLY_securify_account(diana.address(), &shield_3fa) @@ -2657,7 +2679,7 @@ async fn securified_accounts_asymmetric_indices() { .collect::>(), [ ( - fs_device.id_from_hash(), + bdfs.id_from_hash(), HDPathComponent::from_local_key_space( diana_mfa_device, KeySpace::Securified @@ -2665,7 +2687,7 @@ async fn securified_accounts_asymmetric_indices() { .unwrap() ), ( - fs_arculus.id_from_hash(), + arculus.id_from_hash(), HDPathComponent::from_local_key_space( diana_mfa_arculus, KeySpace::Securified @@ -2673,7 +2695,7 @@ async fn securified_accounts_asymmetric_indices() { .unwrap() ), ( - fs_ledger.id_from_hash(), + ledger.id_from_hash(), HDPathComponent::from_local_key_space( diana_mfa_ledger, KeySpace::Securified @@ -2731,21 +2753,21 @@ async fn securified_accounts_asymmetric_indices() { .collect::>(), [ ( - fs_device.id_from_hash(), + bdfs.id_from_hash(), HDPathComponent::Securified( SecurifiedU30::try_from(diana_mfa_device + offset) .unwrap() ) ), ( - fs_arculus.id_from_hash(), + arculus.id_from_hash(), HDPathComponent::Securified( SecurifiedU30::try_from(diana_mfa_arculus + offset) .unwrap() ) ), ( - fs_ledger.id_from_hash(), + ledger.id_from_hash(), HDPathComponent::Securified( SecurifiedU30::try_from(diana_mfa_ledger + offset) .unwrap() diff --git a/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs b/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs index ed729a875..149eacb9d 100644 --- a/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs +++ b/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs @@ -151,97 +151,69 @@ mod tests { #[should_panic] #[actix_rt::test] async fn mfa_panics_if_entities_empty() { - // let fs = FactorSource::sample_at(0); - // let a = Account::sample(); - // let cache_client = FactorInstancesCacheClient::in_memory(); - - // let _ = SUT::for_account_mfa( - // Arc::new(cache_client), - // Arc::new(Profile::sample_from([fs.clone()], [&a], [])), - // MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ) - // .unwrap(), - // IndexSet::::new(), // <---- EMPTY => should_panic - // Arc::new(TestDerivationInteractors::default()), - // ) - // .await - // .unwrap(); - todo!() + let fs = FactorSource::sample_at(0); + let a = Account::sample(); + let cache_client = FactorInstancesCacheClient::in_memory(); + + let _ = SUT::for_account_mfa( + Arc::new(cache_client), + Arc::new(Profile::sample_from([fs.clone()], [&a], [])), + MatrixOfFactorSources::sample(), + IndexSet::::new(), // <---- EMPTY => should_panic + Arc::new(TestDerivationInteractors::default()), + ) + .await + .unwrap(); } #[should_panic] #[actix_rt::test] async fn mfa_panics_if_entity_unknown() { - // let fs = FactorSource::sample_at(0); - // let a = Account::sample(); - // let cache_client = FactorInstancesCacheClient::in_memory(); - - // let _ = SUT::for_account_mfa( - // Arc::new(cache_client), - // Arc::new(Profile::sample_from([fs.clone()], [&a], [])), - // MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ) - // .unwrap(), - // IndexSet::just(Account::sample_other().address()), // <---- unknown => should_panic - // Arc::new(TestDerivationInteractors::default()), - // ) - // .await - // .unwrap(); - todo!() + let fs = FactorSource::sample_at(0); + let a = Account::sample(); + let cache_client = FactorInstancesCacheClient::in_memory(); + let _ = SUT::for_account_mfa( + Arc::new(cache_client), + Arc::new(Profile::sample_from([fs.clone()], [&a], [])), + MatrixOfFactorSources::sample(), + IndexSet::just(Account::sample_other().address()), // <---- unknown => should_panic + Arc::new(TestDerivationInteractors::default()), + ) + .await + .unwrap(); } #[should_panic(expected = "Missing FactorSources")] #[actix_rt::test] async fn mfa_panics_if_factor_source_missing() { - // let fs = FactorSource::sample_at(0); - // let network = NetworkID::Mainnet; - - // let mainnet_account = Account::new(HDFactorInstanceTransactionSigning::new(HierarchicalDeterministicFactorInstance::new_for_entity_on_network( - // network, - // fs.id_from_hash(), - // CAP26EntityKind::Account, - // Hardened::Unsecurified( - // UnsecurifiedHardened::try_from(0u32).unwrap(), - // ), - // )).unwrap(), DisplayName::sample(), AppearanceID::sample()); - - // let profile = Profile::sample_from( - // [], // <---- missing factor source => should_panic - // [&mainnet_account], - // [], - // ); - // let cache_client = FactorInstancesCacheClient::in_memory(); - - // let _ = SUT::for_account_mfa( - // Arc::new(cache_client), - // Arc::new(profile), - // MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ) - // .unwrap(), - // IndexSet::from_iter([mainnet_account.address()]), - // Arc::new(TestDerivationInteractors::default()), - // ) - // .await - // .unwrap(); - todo!() + let fs = FactorSource::sample_at(0); + let network = NetworkID::Mainnet; + + let mainnet_account = Account::new(HDFactorInstanceTransactionSigning::new(HierarchicalDeterministicFactorInstance::new_for_entity_on_network( + network, + fs.id_from_hash(), + CAP26EntityKind::Account, + Hardened::Unsecurified( + UnsecurifiedHardened::try_from(0u32).unwrap(), + ), + )).unwrap(), DisplayName::sample(), AppearanceID::sample()); + + let profile = Profile::sample_from( + [], // <---- missing factor source => should_panic + [&mainnet_account], + [], + ); + let cache_client = FactorInstancesCacheClient::in_memory(); + + let _ = SUT::for_account_mfa( + Arc::new(cache_client), + Arc::new(profile), + MatrixOfFactorSources::sample(), + IndexSet::from_iter([mainnet_account.address()]), + Arc::new(TestDerivationInteractors::default()), + ) + .await + .unwrap(); } #[should_panic] @@ -276,30 +248,21 @@ mod tests { [], ); - // assert_eq!(profile.networks.len(), 2); - // let cache_client = FactorInstancesCacheClient::in_memory(); - - // let _ = SUT::for_account_mfa( - // Arc::new(cache_client), - // Arc::new(profile), - // MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([fs.clone()]) - // .unwrap(), - // ) - // .unwrap(), - // IndexSet::from_iter([ - // mainnet_account.address(), - // stokenet_account.address(), - // ]), // <---- wrong network => should_panic - // Arc::new(TestDerivationInteractors::default()), - // ) - // .await - // .unwrap(); - todo!() + assert_eq!(profile.networks.len(), 2); + let cache_client = FactorInstancesCacheClient::in_memory(); + + let _ = SUT::for_account_mfa( + Arc::new(cache_client), + Arc::new(profile), + MatrixOfFactorSources::sample(), + IndexSet::from_iter([ + mainnet_account.address(), + stokenet_account.address(), + ]), // <---- wrong network => should_panic + Arc::new(TestDerivationInteractors::default()), + ) + .await + .unwrap(); } #[actix_rt::test] @@ -326,50 +289,53 @@ mod tests { .unwrap(); assert!(derivation_outcome.debug_was_derived.is_empty()); - // let matrix_0 = MatrixOfFactorSources::new( - // PrimaryRoleWithFactorSources::override_only([bdfs.clone()]) - // .unwrap(), - // RecoveryRoleWithFactorSources::override_only([bdfs.clone()]) - // .unwrap(), - // ConfirmationRoleWithFactorSources::override_only([bdfs.clone()]) - // .unwrap(), - // ) - // .unwrap(); - - // let cache_client = Arc::new(os.clients.factor_instances_cache.clone()); - // let profile = Arc::new(os.profile().unwrap()); - // let derivation_interactors = os.keys_derivation_interactors(); - - // let (instances_in_cache_consumer, outcome) = SUT::for_account_mfa( - // cache_client.clone(), - // profile, - // matrix_0.clone(), - // IndexSet::just(alice.address()), - // derivation_interactors.clone(), - // ) - // .await - // .unwrap(); - - // // don't forget to consume - // instances_in_cache_consumer.consume().await.unwrap(); - // let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); - // assert_eq!(outcome.to_use_directly.len(), 1); - - // let profile = Arc::new(os.profile().unwrap()); - // let (instances_in_cache_consumer, outcome) = SUT::for_persona_mfa( - // cache_client.clone(), - // profile, - // matrix_0.clone(), - // IndexSet::just(batman.address()), - // derivation_interactors.clone(), - // ) - // .await - // .unwrap(); - - // // don't forget to consume - // instances_in_cache_consumer.consume().await.unwrap(); - // let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); - // assert_eq!(outcome.to_use_directly.len(), 1); - todo!() + os.add_factor_source(FactorSource::sample_ledger()) + .await + .unwrap(); + os.add_factor_source(FactorSource::sample_password()) + .await + .unwrap(); + let factor_sources = &os.profile().unwrap().factor_sources; + let matrix_ids = MatrixTemplate::config_14() + .materialize(factor_sources.items()) + .unwrap(); + + let matrix_0 = + MatrixOfFactorSources::new(matrix_ids, factor_sources).unwrap(); + + let cache_client = Arc::new(os.clients.factor_instances_cache.clone()); + let profile = Arc::new(os.profile().unwrap()); + let derivation_interactors = os.keys_derivation_interactors(); + + let (instances_in_cache_consumer, outcome) = SUT::for_account_mfa( + cache_client.clone(), + profile, + matrix_0.clone(), + IndexSet::just(alice.address()), + derivation_interactors.clone(), + ) + .await + .unwrap(); + + // don't forget to consume + instances_in_cache_consumer.consume().await.unwrap(); + let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); + assert_eq!(outcome.to_use_directly.len(), 1); + + let profile = Arc::new(os.profile().unwrap()); + let (instances_in_cache_consumer, outcome) = SUT::for_persona_mfa( + cache_client.clone(), + profile, + matrix_0.clone(), + IndexSet::just(batman.address()), + derivation_interactors.clone(), + ) + .await + .unwrap(); + + // don't forget to consume + instances_in_cache_consumer.consume().await.unwrap(); + let outcome = outcome.per_factor.get(&bdfs.id_from_hash()).unwrap(); + assert_eq!(outcome.to_use_directly.len(), 1); } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs index 21d6cfcfb..93ea6aa63 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -67,9 +67,18 @@ impl FactorSourceIdAssigner { } impl MatrixTemplate { + pub fn materialize( + self, + factor_source_ids: impl IntoIterator, + ) -> Result { + self.materialize_ids( + factor_source_ids.into_iter().map(|f| f.factor_source_id()), + ) + } + /// Tries to materialize a MatrixOfFactorSourceIds from a MatrixTemplate by /// assigning each template with a concrete FactorSourceID using the `factor_source_ids`.` - pub fn materialize( + pub fn materialize_ids( self, factor_source_ids: impl IntoIterator, ) -> Result { @@ -345,7 +354,7 @@ mod test_templates { expected: MatrixOfFactorSourceIds, ) { let m = template - .materialize(*ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD) + .materialize_ids(*ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD) .unwrap(); pretty_assertions::assert_eq!(m, expected); } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs index 6eef809af..a70826cf1 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs @@ -36,65 +36,63 @@ impl MnemonicWithPassphrase { derivation_presets: impl IntoIterator, sources: impl IntoIterator, ) -> IndexMap { - // let next_index_assigner = NextDerivationEntityIndexAssigner::new( - // network_id, - // None, - // FactorInstancesCache::default(), - // ); - - // let derivation_presets = - // derivation_presets.into_iter().collect::>(); - - // sources - // .into_iter() - // .map(|fs| { - // let fsid = fs.id_from_hash(); - // let mwp = fsid.sample_associated_mnemonic(); - - // let paths = derivation_presets - // .clone() - // .into_iter() - // .map(|dp| (dp, quantity_per_factor)) - // .collect::>(); - - // let paths = paths - // .into_iter() - // .flat_map(|(derivation_preset, qty)| { - // // `qty` many paths - // (0..qty) - // .map(|_| { - // let index_agnostic_path = derivation_preset - // .index_agnostic_path_on_network(network_id); - - // next_index_assigner - // .next(fsid, index_agnostic_path) - // .map(|index| { - // DerivationPath::from(( - // index_agnostic_path, - // index, - // )) - // }) - // .unwrap() - // }) - // .collect::>() - // }) - // .collect::>(); - - // let instances = mwp - // .derive_public_keys(paths) - // .into_iter() - // .map(|public_key| { - // HierarchicalDeterministicFactorInstance::new( - // fsid, public_key, - // ) - // }) - // .collect::(); - - // (fsid, instances) - // }) - // .collect::>() - - todo!("use FIP") + let next_index_assigner = NextDerivationEntityIndexAssigner::new( + network_id, + None, + FactorInstancesCache::default(), + ); + + let derivation_presets = + derivation_presets.into_iter().collect::>(); + + sources + .into_iter() + .map(|fs| { + let fsid = fs.id_from_hash(); + let mwp = fsid.sample_associated_mnemonic(); + + let paths = derivation_presets + .clone() + .into_iter() + .map(|dp| (dp, quantity_per_factor)) + .collect::>(); + + let paths = paths + .into_iter() + .flat_map(|(derivation_preset, qty)| { + // `qty` many paths + (0..qty) + .map(|_| { + let index_agnostic_path = derivation_preset + .index_agnostic_path_on_network(network_id); + + next_index_assigner + .next(fsid, index_agnostic_path) + .map(|index| { + DerivationPath::from(( + index_agnostic_path, + index, + )) + }) + .unwrap() + }) + .collect::>() + }) + .collect::>(); + + let instances = mwp + .derive_public_keys(paths) + .into_iter() + .map(|public_key| { + HierarchicalDeterministicFactorInstance::new( + fsid, public_key, + ) + }) + .collect::(); + + (fsid, instances) + }) + .collect::>() } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index d0c644456..f5f7372dd 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -2,9 +2,8 @@ use crate::prelude::*; pub type MatrixOfFactorSourceIds = AbstractMatrixBuilt; -#[cfg(test)] impl MatrixOfFactorSourceIds { - pub(crate) fn with_roles_and_days( + pub(crate) fn _unvalidated_with_roles_and_days( primary: PrimaryRoleWithFactorSourceIds, recovery: RecoveryRoleWithFactorSourceIds, confirmation: ConfirmationRoleWithFactorSourceIds, @@ -22,6 +21,36 @@ impl MatrixOfFactorSourceIds { } } + pub(crate) fn _unvalidated_with_roles( + primary: PrimaryRoleWithFactorSourceIds, + recovery: RecoveryRoleWithFactorSourceIds, + confirmation: ConfirmationRoleWithFactorSourceIds, + ) -> Self { + Self::_unvalidated_with_roles_and_days( + primary, + recovery, + confirmation, + Self::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + ) + } +} + +#[cfg(test)] +impl MatrixOfFactorSourceIds { + pub(crate) fn with_roles_and_days( + primary: PrimaryRoleWithFactorSourceIds, + recovery: RecoveryRoleWithFactorSourceIds, + confirmation: ConfirmationRoleWithFactorSourceIds, + number_of_days_until_auto_confirm: u16, + ) -> Self { + Self::_unvalidated_with_roles_and_days( + primary, + recovery, + confirmation, + number_of_days_until_auto_confirm, + ) + } + pub(crate) fn with_roles( primary: PrimaryRoleWithFactorSourceIds, recovery: RecoveryRoleWithFactorSourceIds, @@ -39,7 +68,7 @@ impl MatrixOfFactorSourceIds { impl MatrixOfFactorSourceIds { fn sample_from_template(template: MatrixTemplate) -> Self { template - .materialize(*ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD) + .materialize_ids(*ALL_FACTOR_SOURCE_ID_SAMPLES_INC_NON_HD) .unwrap() } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs index b53a5e147..b3012f2b2 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs @@ -3,4 +3,7 @@ mod primary_role_with_factor_sources; mod recovery_role_with_factor_sources; mod roles_with_factor_sources; +pub(crate) use confirmation_role_with_factor_sources::*; +pub(crate) use primary_role_with_factor_sources::*; +pub(crate) use recovery_role_with_factor_sources::*; pub(crate) use roles_with_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs index 4c67e0d8e..5fe53480f 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs @@ -58,9 +58,32 @@ impl From } } +impl From> + for AbstractRoleBuilderOrBuilt +{ + fn from(value: AbstractRoleBuilderOrBuilt) -> Self { + Self::with_factors( + value.get_threshold(), + value + .get_threshold_factors() + .into_iter() + .map(|f| f.factor_source_id()), + value + .get_override_factors() + .into_iter() + .map(|f| f.factor_source_id()), + ) + } +} + impl From for MatrixOfFactorSourceIDs { fn from(value: MatrixOfFactorSources) -> Self { - todo!() + Self::_unvalidated_with_roles_and_days( + PrimaryRoleWithFactorSourceIds::from(value.primary_role), + RecoveryRoleWithFactorSourceIds::from(value.recovery_role), + ConfirmationRoleWithFactorSourceIds::from(value.confirmation_role), + value.number_of_days_until_auto_confirm, + ) } } diff --git a/crates/sargon/src/profile/v100/app_preferences/security.rs b/crates/sargon/src/profile/v100/app_preferences/security.rs index cc1bc40c9..a8816b6f1 100644 --- a/crates/sargon/src/profile/v100/app_preferences/security.rs +++ b/crates/sargon/src/profile/v100/app_preferences/security.rs @@ -171,7 +171,7 @@ mod tests { sut.security_structures_of_factor_source_ids = SecurityStructuresOfFactorSourceIDs::sample(); - + print_json(&sut); assert_eq_after_json_roundtrip( &sut, r#" diff --git a/crates/sargon/src/types/samples/account_samples.rs b/crates/sargon/src/types/samples/account_samples.rs index 0f8b19808..8c718a7ba 100644 --- a/crates/sargon/src/types/samples/account_samples.rs +++ b/crates/sargon/src/types/samples/account_samples.rs @@ -156,42 +156,20 @@ impl Account { veci: HierarchicalDeterministicFactorInstance, make_role: impl Fn() -> GeneralRoleWithHierarchicalDeterministicFactorInstances, ) -> Self { - /* let role = make_role(); - - let threshold_factors = role - .threshold_factors - .iter() - .map(|hd| hd.factor_instance()) - .collect::>(); - - let override_factors = role - .override_factors - .iter() - .map(|hd| hd.factor_instance()) - .collect::>(); - - let matrix = MatrixOfFactorInstances::new( - PrimaryRoleWithFactorInstances::new( - threshold_factors.clone(), - role.threshold, - override_factors.clone(), - ) - .unwrap(), - RecoveryRoleWithFactorInstances::new( - threshold_factors.clone(), - role.threshold, - override_factors.clone(), - ) - .unwrap(), - ConfirmationRoleWithFactorInstances::new( - threshold_factors.clone(), - role.threshold, - override_factors.clone(), - ) - .unwrap(), - ) - .unwrap(); + assert_eq!(role.get_role_kind(), RoleKind::Primary, "If this tests fails you can update the code below to not be hardcoded to set the primary role..."); + let mut matrix = MatrixOfFactorInstances::sample(); + matrix.primary_role = PrimaryRoleWithFactorInstances::with_factors( + role.get_threshold(), + role.get_threshold_factors() + .into_iter() + .map(FactorInstance::from) + .collect_vec(), + role.get_override_factors() + .into_iter() + .map(FactorInstance::from) + .collect_vec(), + ); let network_id = NetworkID::Mainnet; let address = @@ -214,8 +192,6 @@ impl Account { flags: Default::default(), on_ledger_settings: Default::default(), } - */ - unimplemented!("TODO MFA-Role Rules"); } pub fn sample_at(index: usize) -> Self { diff --git a/crates/sargon/src/types/samples/persona_samples.rs b/crates/sargon/src/types/samples/persona_samples.rs index 1586dbaad..278b7dd69 100644 --- a/crates/sargon/src/types/samples/persona_samples.rs +++ b/crates/sargon/src/types/samples/persona_samples.rs @@ -121,42 +121,20 @@ impl Persona { veci: HierarchicalDeterministicFactorInstance, make_role: impl Fn() -> GeneralRoleWithHierarchicalDeterministicFactorInstances, ) -> Self { - /* let role = make_role(); - - let threshold_factors = role - .threshold_factors - .iter() - .map(|hd| hd.factor_instance()) - .collect::>(); - - let override_factors = role - .override_factors - .iter() - .map(|hd| hd.factor_instance()) - .collect::>(); - - let matrix = MatrixOfFactorInstances::new( - PrimaryRoleWithFactorInstances::new( - threshold_factors.clone(), - role.threshold, - override_factors.clone(), - ) - .unwrap(), - RecoveryRoleWithFactorInstances::new( - threshold_factors.clone(), - role.threshold, - override_factors.clone(), - ) - .unwrap(), - ConfirmationRoleWithFactorInstances::new( - threshold_factors.clone(), - role.threshold, - override_factors.clone(), - ) - .unwrap(), - ) - .unwrap(); + assert_eq!(role.get_role_kind(), RoleKind::Primary, "If this tests fails you can update the code below to not be hardcoded to set the primary role..."); + let mut matrix = MatrixOfFactorInstances::sample(); + matrix.primary_role = PrimaryRoleWithFactorInstances::with_factors( + role.get_threshold(), + role.get_threshold_factors() + .into_iter() + .map(FactorInstance::from) + .collect_vec(), + role.get_override_factors() + .into_iter() + .map(FactorInstance::from) + .collect_vec(), + ); let address = IdentityAddress::new(veci.public_key(), NetworkID::Mainnet); Self { @@ -176,8 +154,6 @@ impl Persona { flags: Default::default(), persona_data: Default::default(), } - */ - unimplemented!("migrate MFA-Role Rules") } pub fn sample_at(index: usize) -> Self { From 04d4fc3a8f930bb56936271591b935469049c8ed Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 30 Nov 2024 18:09:08 +0100 Subject: [PATCH 03/70] sargon works --- .../security_structure_of_factor_sources.rs | 4 +- .../profile/v100/app_preferences/security.rs | 115 ++++-------------- .../entity_security_state.rs | 79 ++++-------- .../petition_types/petition_for_entity.rs | 39 ------ 4 files changed, 52 insertions(+), 185 deletions(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs index 5fe53480f..0932e096d 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs @@ -66,11 +66,11 @@ impl From> value.get_threshold(), value .get_threshold_factors() - .into_iter() + .iter() .map(|f| f.factor_source_id()), value .get_override_factors() - .into_iter() + .iter() .map(|f| f.factor_source_id()), ) } diff --git a/crates/sargon/src/profile/v100/app_preferences/security.rs b/crates/sargon/src/profile/v100/app_preferences/security.rs index a8816b6f1..2dcdbfbff 100644 --- a/crates/sargon/src/profile/v100/app_preferences/security.rs +++ b/crates/sargon/src/profile/v100/app_preferences/security.rs @@ -171,11 +171,11 @@ mod tests { sut.security_structures_of_factor_source_ids = SecurityStructuresOfFactorSourceIDs::sample(); - print_json(&sut); + assert_eq_after_json_roundtrip( &sut, r#" - { + { "isCloudProfileSyncEnabled": true, "isDeveloperModeEnabled": false, "isAdvancedLockEnabled": false, @@ -187,9 +187,9 @@ mod tests { "createdOn": "2023-09-11T16:05:56.000Z", "lastUpdatedOn": "2023-09-11T16:05:56.000Z" }, - "numberOfEpochsUntilAutoConfirmation": 4032, "matrixOfFactors": { "primaryRole": { + "threshold": 2, "thresholdFactors": [ { "discriminator": "fromHash", @@ -198,23 +198,6 @@ mod tests { "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" } }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "arculusCard", - "body": "12f36554769cd96614776e6dbd5629825b8e87366eec5e515de32bb1ea153820" - } - }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "offDeviceMnemonic", - "body": "820122c9573768ab572b0c9fa492a45b7b451a2740291b3da908ad423d10e410" - } - } - ], - "threshold": 2, - "overrideFactors": [ { "discriminator": "fromHash", "fromHash": { @@ -222,27 +205,20 @@ mod tests { "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" } } - ] + ], + "overrideFactors": [] }, "recoveryRole": { - "thresholdFactors": [ + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ { "discriminator": "fromHash", "fromHash": { - "kind": "arculusCard", - "body": "12f36554769cd96614776e6dbd5629825b8e87366eec5e515de32bb1ea153820" + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" } }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "arculusCard", - "body": "3ac064d4b40f78effe7037a12f3287efc67aa87af7c6a083738eae05e28dadaf" - } - } - ], - "threshold": 2, - "overrideFactors": [ { "discriminator": "fromHash", "fromHash": { @@ -253,25 +229,19 @@ mod tests { ] }, "confirmationRole": { - "thresholdFactors": [], "threshold": 0, + "thresholdFactors": [], "overrideFactors": [ { "discriminator": "fromHash", "fromHash": { - "kind": "securityQuestions", - "body": "aabc6041d95785ecfabe7d5ed5af259e20e4e3f5f95b16fdeca386bc75796b46" - } - }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "ledgerHQHardwareWallet", - "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + "kind": "password", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" } } ] - } + }, + "numberOfDaysUntilAutoConfirm": 14 } }, { @@ -281,75 +251,37 @@ mod tests { "createdOn": "2023-12-24T17:13:56.123Z", "lastUpdatedOn": "2023-12-24T17:13:56.123Z" }, - "numberOfEpochsUntilAutoConfirmation": 8064, "matrixOfFactors": { "primaryRole": { + "threshold": 1, "thresholdFactors": [ { "discriminator": "fromHash", "fromHash": { "kind": "device", - "body": "5255999c65076ce9ced5a1881f1a621bba1ce3f1f68a61df462d96822a5190cd" - } - }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "arculusCard", - "body": "3ac064d4b40f78effe7037a12f3287efc67aa87af7c6a083738eae05e28dadaf" - } - }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "offDeviceMnemonic", - "body": "5c308b9c3e41912d4af4c5ff088e84877aac5de01c95f32dedd280d55a6d8262" + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" } } ], - "threshold": 2, - "overrideFactors": [ - { - "discriminator": "fromHash", - "fromHash": { - "kind": "ledgerHQHardwareWallet", - "body": "52ef052a0642a94279b296d6b3b17dedc035a7ae37b76c1d60f11f2725100077" - } - } - ] + "overrideFactors": [] }, "recoveryRole": { - "thresholdFactors": [ - { - "discriminator": "fromHash", - "fromHash": { - "kind": "arculusCard", - "body": "3ac064d4b40f78effe7037a12f3287efc67aa87af7c6a083738eae05e28dadaf" - } - } - ], - "threshold": 1, + "threshold": 0, + "thresholdFactors": [], "overrideFactors": [ { "discriminator": "fromHash", "fromHash": { "kind": "ledgerHQHardwareWallet", - "body": "52ef052a0642a94279b296d6b3b17dedc035a7ae37b76c1d60f11f2725100077" + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" } } ] }, "confirmationRole": { - "thresholdFactors": [], "threshold": 0, + "thresholdFactors": [], "overrideFactors": [ - { - "discriminator": "fromHash", - "fromHash": { - "kind": "securityQuestions", - "body": "bb0ac72196f748bba4ddf9c6d87c4e3ea939750e3a207f312653aa25f3f9c060" - } - }, { "discriminator": "fromHash", "fromHash": { @@ -358,7 +290,8 @@ mod tests { } } ] - } + }, + "numberOfDaysUntilAutoConfirm": 14 } } ] diff --git a/crates/sargon/src/profile/v100/entity_security_state/entity_security_state.rs b/crates/sargon/src/profile/v100/entity_security_state/entity_security_state.rs index 63ee014ad..240992d17 100644 --- a/crates/sargon/src/profile/v100/entity_security_state/entity_security_state.rs +++ b/crates/sargon/src/profile/v100/entity_security_state/entity_security_state.rs @@ -155,7 +155,7 @@ mod tests { assert_eq_after_json_roundtrip( &model, r#" - { + { "discriminator": "securified", "securedEntityControl": { "veci": null, @@ -164,6 +164,7 @@ mod tests { "securityStructureId": "ffffffff-ffff-ffff-ffff-ffffffffffff", "matrixOfFactors": { "primaryRole": { + "threshold": 2, "thresholdFactors": [ { "factorSourceID": { @@ -189,16 +190,13 @@ mod tests { } } } - } - ], - "threshold": 1, - "overrideFactors": [ + }, { "factorSourceID": { "discriminator": "fromHash", "fromHash": { - "kind": "device", - "body": "5255999c65076ce9ced5a1881f1a621bba1ce3f1f68a61df462d96822a5190cd" + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" } }, "badge": { @@ -208,7 +206,7 @@ mod tests { "hierarchicalDeterministicPublicKey": { "publicKey": { "curve": "curve25519", - "compressedData": "e0293d4979bc303ea4fe361a62baf9c060c7d90267972b05c61eead9ef3eed3e" + "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" }, "derivationPath": { "scheme": "cap26", @@ -218,10 +216,13 @@ mod tests { } } } - ] + ], + "overrideFactors": [] }, "recoveryRole": { - "thresholdFactors": [ + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ { "factorSourceID": { "discriminator": "fromHash", @@ -237,25 +238,22 @@ mod tests { "hierarchicalDeterministicPublicKey": { "publicKey": { "curve": "curve25519", - "compressedData": "161a65a7b4f374d81bf5e7f73669f5b09b684a860812ec1a34f3220b6ffe8dcf" + "compressedData": "427969814e15d74c3ff4d9971465cb709d210c8a7627af9466bdaa67bd0929b7" }, "derivationPath": { "scheme": "cap26", - "path": "m/44H/1022H/1H/525H/1460H/54S" + "path": "m/44H/1022H/1H/525H/1460H/0S" } } } } - } - ], - "threshold": 1, - "overrideFactors": [ + }, { "factorSourceID": { "discriminator": "fromHash", "fromHash": { - "kind": "device", - "body": "5255999c65076ce9ced5a1881f1a621bba1ce3f1f68a61df462d96822a5190cd" + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" } }, "badge": { @@ -265,11 +263,11 @@ mod tests { "hierarchicalDeterministicPublicKey": { "publicKey": { "curve": "curve25519", - "compressedData": "23fa85f95c79684d2768f46ec4379b5e031757b727f76cfd01a50bd4cf8afcff" + "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" }, "derivationPath": { "scheme": "cap26", - "path": "m/44H/1022H/1H/525H/1460H/237S" + "path": "m/44H/1022H/1H/525H/1460H/0S" } } } @@ -278,41 +276,15 @@ mod tests { ] }, "confirmationRole": { - "thresholdFactors": [ - { - "factorSourceID": { - "discriminator": "fromHash", - "fromHash": { - "kind": "device", - "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" - } - }, - "badge": { - "discriminator": "virtualSource", - "virtualSource": { - "discriminator": "hierarchicalDeterministicPublicKey", - "hierarchicalDeterministicPublicKey": { - "publicKey": { - "curve": "curve25519", - "compressedData": "0f081cd5f944efc9cae2f3262e30b445b947601b2fc668938a7c4d464c88fe69" - }, - "derivationPath": { - "scheme": "cap26", - "path": "m/44H/1022H/1H/525H/1460H/27S" - } - } - } - } - } - ], - "threshold": 1, + "threshold": 0, + "thresholdFactors": [], "overrideFactors": [ { "factorSourceID": { "discriminator": "fromHash", "fromHash": { - "kind": "device", - "body": "5255999c65076ce9ced5a1881f1a621bba1ce3f1f68a61df462d96822a5190cd" + "kind": "password", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" } }, "badge": { @@ -322,18 +294,19 @@ mod tests { "hierarchicalDeterministicPublicKey": { "publicKey": { "curve": "curve25519", - "compressedData": "d3d66160cf7117b310c7875fbf8b5695ccc13116a167d13196d22dd8be18a60f" + "compressedData": "4af49eb56b1af579aaf03f1760ec526f56e2297651f7a067f4b362f685417a81" }, "derivationPath": { "scheme": "cap26", - "path": "m/44H/1022H/1H/525H/1460H/13S" + "path": "m/44H/1022H/1H/525H/1460H/0S" } } } } } ] - } + }, + "numberOfDaysUntilAutoConfirm": 14 } } } diff --git a/crates/sargon/src/signing/petition_types/petition_for_entity.rs b/crates/sargon/src/signing/petition_types/petition_for_entity.rs index 9039c2bc0..d7532d1b7 100644 --- a/crates/sargon/src/signing/petition_types/petition_for_entity.rs +++ b/crates/sargon/src/signing/petition_types/petition_for_entity.rs @@ -584,45 +584,6 @@ mod tests { sut.add_signature(HDSignature::sample()); } - #[test] - fn factor_should_not_be_used_in_both_lists() { - let fi = HierarchicalDeterministicFactorInstance::sample_id_to_instance( - CAP26EntityKind::Account, - Hardened::from_local_key_space(0, IsSecurified(true)).unwrap(), - ); - assert_eq!( - GeneralRoleWithHierarchicalDeterministicFactorInstances::with_factors_and_role( - RoleKind::Primary, - [FactorSourceIDFromHash::sample_at(0)].map(&fi), - 1, - [FactorSourceIDFromHash::sample_at(0)].map(&fi), - ), - Err(CommonError::InvalidSecurityStructureFactorInBothThresholdAndOverride) - ); - } - - #[test] - fn threshold_should_not_be_bigger_than_threshold_factors() { - let fi = HierarchicalDeterministicFactorInstance::sample_id_to_instance( - CAP26EntityKind::Account, - Hardened::from_local_key_space(0, IsSecurified(true)).unwrap(), - ); - assert_eq!( - GeneralRoleWithHierarchicalDeterministicFactorInstances::with_factors_and_role( - RoleKind::Primary, - [FactorSourceIDFromHash::sample_at(0)].map(&fi), - 2, - [], - ), - Err( - CommonError::InvalidSecurityStructureThresholdExceedsFactors { - threshold: 2, - factors: 1, - } - ) - ); - } - #[test] #[should_panic] fn cannot_add_same_signature_twice() { From 38acdc7982ff1af61c94d5efa57ffd7469ab5296 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 10:43:49 +0100 Subject: [PATCH 04/70] [no ci] WIP --- Cargo.lock | 4 +- crates/sargon-uniffi/Cargo.toml | 2 +- .../mfa/security_structures/builder.rs | 140 +++++++++++------- .../security_structures/error_conversion.rs | 23 +-- .../matrices/matrix_of_factor_instances.rs | 92 ++++++++++++ .../matrices/matrix_of_factor_source_ids.rs | 92 ++++++++++++ .../matrices/matrix_of_factor_sources.rs | 24 +++ .../mfa/security_structures/matrices/mod.rs | 7 + .../profile/mfa/security_structures/mod.rs | 13 +- ...ource_in_role_builder_validation_status.rs | 18 ++- .../mfa/security_structures/models/mod.rs | 2 + .../security_structures/models/role_kind.rs | 12 ++ .../roles/decl_role_macro.rs | 91 ++++++++++++ .../mfa/security_structures/roles/mod.rs | 8 + .../roles/roles_factor_instances.rs | 5 + .../roles/roles_factor_source_ids.rs | 5 + .../roles/roles_factor_sources.rs | 5 + .../security_structures/mod.rs | 7 + .../security_structure_of_factor_instances.rs | 14 ++ ...security_structure_of_factor_source_ids.rs | 2 + .../security_structure_of_factor_sources.rs | 28 ++-- crates/sargon/Cargo.toml | 2 +- .../matrices/factor_source_id_samples.rs | 28 ++-- .../mfa/security_structures/matrices/mod.rs | 2 +- .../roles/abstract_role_builder_or_built.rs | 12 +- ...confirmation_role_with_factor_instances.rs | 2 +- .../factor_instance_level/mod.rs | 8 +- .../primary_role_with_factor_instances.rs | 2 +- .../recovery_role_with_factor_instances.rs | 2 +- ...onfirmation_role_with_factor_source_ids.rs | 3 +- .../primary_role_with_factor_source_ids.rs | 3 +- .../recovery_role_with_factor_source_ids.rs | 3 +- .../factor_source_kind_level/role_template.rs | 6 +- .../confirmation_role_with_factor_sources.rs | 2 +- .../factor_levels/factor_source_level/mod.rs | 8 +- .../primary_role_with_factor_sources.rs | 3 +- .../recovery_role_with_factor_sources.rs | 2 +- .../roles/factor_levels/mod.rs | 2 +- .../mfa/security_structures/roles/mod.rs | 2 +- 39 files changed, 548 insertions(+), 138 deletions(-) create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/mod.rs rename crates/sargon-uniffi/src/profile/mfa/security_structures/{ => security_structures}/security_structure_of_factor_instances.rs (56%) rename crates/sargon-uniffi/src/profile/mfa/security_structures/{ => security_structures}/security_structure_of_factor_source_ids.rs (84%) rename crates/sargon-uniffi/src/profile/mfa/security_structures/{ => security_structures}/security_structure_of_factor_sources.rs (52%) diff --git a/Cargo.lock b/Cargo.lock index 57b109686..8d8597e9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2759,7 +2759,7 @@ dependencies = [ [[package]] name = "sargon" -version = "1.1.70" +version = "1.1.71" dependencies = [ "actix-rt", "aes-gcm", @@ -2814,7 +2814,7 @@ dependencies = [ [[package]] name = "sargon-uniffi" -version = "1.1.70" +version = "1.1.71" dependencies = [ "actix-rt", "assert-json-diff", diff --git a/crates/sargon-uniffi/Cargo.toml b/crates/sargon-uniffi/Cargo.toml index 61348b1f2..1183ada80 100644 --- a/crates/sargon-uniffi/Cargo.toml +++ b/crates/sargon-uniffi/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sargon-uniffi" # Don't forget to update version in crates/sargon/Cargo.toml -version = "1.1.70" +version = "1.1.71" edition = "2021" build = "build.rs" diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs index 5e24d63f8..2a5804d74 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -2,9 +2,12 @@ #![allow(dead_code)] #![allow(unused_variables)] -use std::sync::{Arc, RwLock}; +use std::{ + borrow::Borrow, + sync::{Arc, RwLock}, +}; -use sargon::IndexSet; +use sargon::{IndexSet, MatrixBuilder}; use crate::prelude::*; @@ -17,7 +20,7 @@ pub struct SecurityShieldBuilder { #[derive(Debug, PartialEq, Eq, Hash, uniffi::Object)] #[uniffi::export(Debug, Eq, Hash)] pub struct SecurityStructureOfFactorSourceIds { - pub wrapped: rules::SecurityStructureOfFactorSourceIds, + pub wrapped: sargon::SecurityStructureOfFactorSourceIds, } impl SecurityShieldBuilder { @@ -41,11 +44,10 @@ impl SecurityShieldBuilder { ) -> Result { let guard = self.wrapped.write(); - let mut binding = - guard.map_err(|_| CommonError::MatrixBuilderRwLockPoisoned)?; + let mut binding = guard.map_err(|_| CommonError::Unknown)?; // TODO: CommonError::MatrixBuilderRwLockPoisoned let Some(builder) = binding.as_mut() else { - return Err(CommonError::AlreadyBuilt); + return Err(CommonError::Unknown); // TODO: CommonError::AlreadyBuilt }; with_non_consumed_builder(builder) .map_err(|e| Into::::into(e)) @@ -53,17 +55,17 @@ impl SecurityShieldBuilder { fn validation_for_addition_of_factor_source_by_calling( &self, - factor_sources: Vec>, + factor_sources: Vec, call: impl Fn( &MatrixBuilder, &IndexSet, ) - -> IndexSet, + -> IndexSet, ) -> Result>, CommonError> { let input = &factor_sources .clone() .into_iter() - .map(|x| x.inner) + .map(Into::::into) .collect::>(); self.with(|builder| { let xs = call(builder, input); @@ -94,10 +96,13 @@ impl SecurityShieldBuilder { fn get_factors( &self, access: impl Fn(&MatrixBuilder) -> &Vec, - ) -> Vec> { + ) -> Vec { self.get(|builder| { let factors = access(builder); - factors.iter().map(FactorSourceID::new).collect::>() + factors + .iter() + .map(|x| crate::FactorSourceID::from(x.clone())) + .collect::>() }) } } @@ -119,19 +124,19 @@ impl SecurityShieldBuilder { self.name.read().unwrap().clone() } - pub fn get_primary_threshold_factors(&self) -> Vec> { + pub fn get_primary_threshold_factors(&self) -> Vec { self.get_factors(|builder| builder.get_primary_threshold_factors()) } - pub fn get_primary_override_factors(&self) -> Vec> { + pub fn get_primary_override_factors(&self) -> Vec { self.get_factors(|builder| builder.get_primary_override_factors()) } - pub fn get_recovery_factors(&self) -> Vec> { + pub fn get_recovery_factors(&self) -> Vec { self.get_factors(|builder| builder.get_recovery_factors()) } - pub fn get_confirmation_factors(&self) -> Vec> { + pub fn get_confirmation_factors(&self) -> Vec { self.get_factors(|builder| builder.get_confirmation_factors()) } } @@ -148,29 +153,33 @@ impl SecurityShieldBuilder { /// Adds the factor source to the primary role threshold list. pub fn add_factor_source_to_primary_threshold( &self, - factor_source_id: Arc, + factor_source_id: FactorSourceID, ) -> Result<(), CommonError> { self.with(|builder| { - builder - .add_factor_source_to_primary_threshold(factor_source_id.inner) + builder.add_factor_source_to_primary_threshold( + factor_source_id.clone().into(), + ) }) } pub fn add_factor_source_to_primary_override( &self, - factor_source_id: Arc, + factor_source_id: FactorSourceID, ) -> Result<(), CommonError> { self.with(|builder| { - builder - .add_factor_source_to_primary_override(factor_source_id.inner) + builder.add_factor_source_to_primary_override( + factor_source_id.clone().into(), + ) }) } pub fn remove_factor( &self, - factor_source_id: Arc, + factor_source_id: FactorSourceID, ) -> Result<(), CommonError> { - self.with(|builder| builder.remove_factor(&factor_source_id.inner)) + self.with(|builder| { + builder.remove_factor(&factor_source_id.clone().into()) + }) } pub fn set_threshold(&self, threshold: u8) -> Result<(), CommonError> { @@ -188,21 +197,22 @@ impl SecurityShieldBuilder { pub fn add_factor_source_to_recovery_override( &self, - factor_source_id: Arc, + factor_source_id: FactorSourceID, ) -> Result<(), CommonError> { self.with(|builder| { - builder - .add_factor_source_to_recovery_override(factor_source_id.inner) + builder.add_factor_source_to_recovery_override( + factor_source_id.clone().into(), + ) }) } pub fn add_factor_source_to_confirmation_override( &self, - factor_source_id: Arc, + factor_source_id: FactorSourceID, ) -> Result<(), CommonError> { self.with(|builder| { builder.add_factor_source_to_confirmation_override( - factor_source_id.inner, + factor_source_id.clone().into(), ) }) } @@ -213,7 +223,7 @@ impl SecurityShieldBuilder { ) -> Result<(), CommonError> { self.with(|builder| { builder.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( - factor_source_kind.into(), + factor_source_kind.clone().into(), ) }) } @@ -224,7 +234,7 @@ impl SecurityShieldBuilder { ) -> Result<(), CommonError> { self.with(|builder| { builder.validation_for_addition_of_factor_source_of_kind_to_recovery_override( - factor_source_kind.into(), + factor_source_kind.clone().into(), ) }) } @@ -235,7 +245,7 @@ impl SecurityShieldBuilder { ) -> Result<(), CommonError> { self.with(|builder| { builder.validation_for_addition_of_factor_source_of_kind_to_primary_override( - factor_source_kind.into(), + factor_source_kind.clone().into(), ) }) } @@ -246,14 +256,14 @@ impl SecurityShieldBuilder { ) -> Result<(), CommonError> { self.with(|builder| { builder.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( - factor_source_kind.into(), + factor_source_kind.clone().into(), ) }) } pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( &self, - factor_sources: Vec>, + factor_sources: Vec, ) -> Result>, CommonError> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, @@ -266,7 +276,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( &self, - factor_sources: Vec>, + factor_sources: Vec, ) -> Result>, CommonError> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, @@ -278,7 +288,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( &self, - factor_sources: Vec>, + factor_sources: Vec, ) -> Result>, CommonError> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, @@ -291,7 +301,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( &self, - factor_sources: Vec>, + factor_sources: Vec, ) -> Result>, CommonError> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, @@ -306,19 +316,16 @@ impl SecurityShieldBuilder { pub fn build( self: Arc, ) -> Result { - let mut binding = self - .wrapped - .write() - .map_err(|_| CommonError::MatrixBuilderRwLockPoisoned)?; - let builder = binding.take().ok_or(CommonError::AlreadyBuilt)?; - let wrapped_matrix = builder - .build() - .map_err(|e| CommonError::BuildError(format!("{:?}", e)))?; + let mut binding = + self.wrapped.write().map_err(|_| CommonError::Unknown)?; // TODO: CommonError::MatrixBuilderRwLockPoisoned + let builder = binding.take().ok_or(CommonError::Unknown)?; // TODO: CommonError::AlreadyBuilt + let wrapped_matrix = + builder.build().map_err(|e| CommonError::Unknown)?; // TODO: CommonError::BuildError(format!("{:?}", e) let name = self.get_name(); - let display_name = sargon::DisplayName::new(name) - .map_err(|e| CommonError::Sargon(format!("{:?}", e)))?; - let wrapped_shield = rules::SecurityStructureOfFactorSourceIds::new( + let display_name = + sargon::DisplayName::new(name).map_err(|e| CommonError::Unknown)?; // TODO CommonError display.. + let wrapped_shield = sargon::SecurityStructureOfFactorSourceIds::new( display_name, wrapped_matrix, ); @@ -330,6 +337,39 @@ impl SecurityShieldBuilder { } } +impl FactorSourceID { + pub fn new(inner: impl Borrow) -> Self { + Self::from(inner.borrow().clone()) + } +} + +#[cfg(test)] +impl FactorSourceID { + pub fn sample_device() -> Self { + Self::new(sargon::FactorSourceID::sample_device()) + } + + pub fn sample_device_other() -> Self { + Self::new(sargon::FactorSourceID::sample_device_other()) + } + + pub fn sample_ledger() -> Self { + Self::new(sargon::FactorSourceID::sample_ledger()) + } + + pub fn sample_ledger_other() -> Self { + Self::new(sargon::FactorSourceID::sample_ledger_other()) + } + + pub fn sample_arculus() -> Self { + Self::new(sargon::FactorSourceID::sample_arculus()) + } + + pub fn sample_arculus_other() -> Self { + Self::new(sargon::FactorSourceID::sample_arculus_other()) + } +} + #[cfg(test)] mod tests { @@ -515,7 +555,7 @@ mod tests { .matrix_of_factors .primary() .get_override_factors(), - &vec![FactorSourceID::sample_arculus().inner] + &vec![FactorSourceID::sample_arculus().into()] ); assert_eq!( shield @@ -523,7 +563,7 @@ mod tests { .matrix_of_factors .recovery() .get_override_factors(), - &vec![FactorSourceID::sample_ledger().inner] + &vec![FactorSourceID::sample_ledger().into()] ); assert_eq!( shield @@ -531,7 +571,7 @@ mod tests { .matrix_of_factors .confirmation() .get_override_factors(), - &vec![FactorSourceID::sample_device().inner] + &vec![FactorSourceID::sample_device().into()] ); } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs index 7022a6218..6255b5c99 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs @@ -1,28 +1,17 @@ -use crate::prelude::*; - -#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, uniffi::Error)] -pub enum CommonError { - #[error("Sargon")] - Sargon(String), - - #[error("AlreadyBuilt")] - AlreadyBuilt, +use sargon::{MatrixBuilderValidation, RoleBuilderValidation}; - #[error("Matrix builder RwLock poisoned")] - MatrixBuilderRwLockPoisoned, - - #[error("Build error {0}")] - BuildError(String), -} +use crate::prelude::*; impl From for CommonError { fn from(val: MatrixBuilderValidation) -> Self { - CommonError::BuildError(format!("{:?}", val)) + // CommonError::BuildError(format!("{:?}", val)) + CommonError::Unknown } } impl From for CommonError { fn from(val: RoleBuilderValidation) -> Self { - CommonError::BuildError(format!("{:?}", val)) + // CommonError::BuildError(format!("{:?}", val)) + CommonError::Unknown } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs new file mode 100644 index 000000000..4a32536d1 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs @@ -0,0 +1,92 @@ +use crate::prelude::*; + +use sargon::ConfirmationRoleWithFactorInstances as InternalConfirmationRoleWithFactorInstances; +use sargon::MatrixOfFactorInstances as InternalMatrixOfFactorInstances; +use sargon::PrimaryRoleWithFactorInstances as InternalPrimaryRoleWithFactorInstances; +use sargon::RecoveryRoleWithFactorInstances as InternalRecoveryRoleWithFactorInstances; + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct PrimaryRoleWithFactorInstances { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +// impl From +// for PrimaryRoleWithFactorInstances +// { +// fn from(value: InternalPrimaryRoleWithFactorInstances) -> Self { +// todo!() +// } +// } +// impl From +// for InternalPrimaryRoleWithFactorInstances +// { +// fn from(value: PrimaryRoleWithFactorInstances) -> Self { +// todo!() +// } +// } + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct RecoveryRoleWithFactorInstances { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +// impl From +// for RecoveryRoleWithFactorInstances +// { +// fn from(value: InternalRecoveryRoleWithFactorInstances) -> Self { +// todo!() +// } +// } +// impl From +// for InternalRecoveryRoleWithFactorInstances +// { +// fn from(value: RecoveryRoleWithFactorInstances) -> Self { +// todo!() +// } +// } + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct ConfirmationRoleWithFactorInstances { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +// impl From +// for ConfirmationRoleWithFactorInstances +// { +// fn from(value: InternalConfirmationRoleWithFactorInstances) -> Self { +// todo!() +// } +// } +// impl From +// for InternalConfirmationRoleWithFactorInstances +// { +// fn from(value: ConfirmationRoleWithFactorInstances) -> Self { +// todo!() +// } +// } + +#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +pub struct MatrixOfFactorInstances { + pub primary_role: PrimaryRoleWithFactorInstances, + pub recovery_role: RecoveryRoleWithFactorInstances, + pub confirmation_role: ConfirmationRoleWithFactorInstances, + + pub number_of_days_until_auto_confirm: u16, +} + +impl From for MatrixOfFactorInstances { + fn from(value: InternalMatrixOfFactorInstances) -> Self { + todo!() + } +} +impl From for InternalMatrixOfFactorInstances { + fn from(value: MatrixOfFactorInstances) -> Self { + todo!() + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs new file mode 100644 index 000000000..18c28bb0c --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -0,0 +1,92 @@ +use crate::prelude::*; + +use sargon::ConfirmationRoleWithFactorSourceIds as InternalConfirmationRoleWithFactorSourceIds; +use sargon::MatrixOfFactorSourceIds as InternalMatrixOfFactorSourceIds; +use sargon::PrimaryRoleWithFactorSourceIds as InternalPrimaryRoleWithFactorSourceIds; +use sargon::RecoveryRoleWithFactorSourceIds as InternalRecoveryRoleWithFactorSourceIds; + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct PrimaryRoleWithFactorSourceIds { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +// impl From +// for PrimaryRoleWithFactorSourceIds +// { +// fn from(value: InternalPrimaryRoleWithFactorSourceIds) -> Self { +// todo!() +// } +// } +// impl From +// for InternalPrimaryRoleWithFactorSourceIds +// { +// fn from(value: PrimaryRoleWithFactorSourceIds) -> Self { +// todo!() +// } +// } + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct RecoveryRoleWithFactorSourceIds { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +// impl From +// for RecoveryRoleWithFactorSourceIds +// { +// fn from(value: InternalRecoveryRoleWithFactorSourceIds) -> Self { +// todo!() +// } +// } +// impl From +// for InternalRecoveryRoleWithFactorSourceIds +// { +// fn from(value: RecoveryRoleWithFactorSourceIds) -> Self { +// todo!() +// } +// } + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct ConfirmationRoleWithFactorSourceIds { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +// impl From +// for ConfirmationRoleWithFactorSourceIds +// { +// fn from(value: InternalConfirmationRoleWithFactorSourceIds) -> Self { +// todo!() +// } +// } +// impl From +// for InternalConfirmationRoleWithFactorSourceIds +// { +// fn from(value: ConfirmationRoleWithFactorSourceIds) -> Self { +// todo!() +// } +// } + +#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +pub struct MatrixOfFactorSourceIds { + pub primary_role: PrimaryRoleWithFactorSourceIDs, + pub recovery_role: RecoveryRoleWithFactorSourceIDs, + pub confirmation_role: ConfirmationRoleWithFactorSourceIDs, + + pub number_of_days_until_auto_confirm: u16, +} + +impl From for MatrixOfFactorSourceIds { + fn from(value: InternalMatrixOfFactorSourceIds) -> Self { + todo!() + } +} +impl From for InternalMatrixOfFactorSourceIds { + fn from(value: MatrixOfFactorSourceIds) -> Self { + todo!() + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs new file mode 100644 index 000000000..48989a7df --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs @@ -0,0 +1,24 @@ +use crate::prelude::*; + +use sargon::MatrixOfFactorSources as InternalMatrixOfFactorSources; + + +#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +pub struct MatrixOfFactorSources { + pub primary_role: PrimaryRoleWithFactorSources, + pub recovery_role: RecoveryRoleWithFactorSources, + pub confirmation_role: ConfirmationRoleWithFactorSources, + + pub number_of_days_until_auto_confirm: u16, +} + +impl From for MatrixOfFactorSources { + fn from(value: InternalMatrixOfFactorSources) -> Self { + todo!() + } +} +impl From for InternalMatrixOfFactorSources { + fn from(value: MatrixOfFactorSources) -> Self { + todo!() + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs new file mode 100644 index 000000000..99e061a89 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs @@ -0,0 +1,7 @@ +mod matrix_of_factor_instances; +mod matrix_of_factor_source_ids; +mod matrix_of_factor_sources; + +pub use matrix_of_factor_instances::*; +pub use matrix_of_factor_source_ids::*; +pub use matrix_of_factor_sources::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index 945274071..8ff99088d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,14 +1,15 @@ mod builder; mod error_conversion; +mod matrices; mod models; +mod roles; mod security_structure_id; mod security_structure_metadata; -mod security_structure_of_factor_instances; -mod security_structure_of_factor_source_ids; -mod security_structure_of_factor_sources; +mod security_structures; +pub use matrices::*; +pub use models::*; +pub use roles::*; pub use security_structure_id::*; pub use security_structure_metadata::*; -pub use security_structure_of_factor_instances::*; -pub use security_structure_of_factor_source_ids::*; -pub use security_structure_of_factor_sources::*; +pub use security_structures::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 1f6b352ca..5bcee31b2 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -1,17 +1,19 @@ -#[derive(Debug, Clone, PartialEq, Eq, Hash, uniffi::Object)] +use crate::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Object)] pub struct FactorSourceValidationStatus { - pub role: sargon::RoleKind, - pub factor_source_id: sargon::FactorSourceID, - pub validation: rules::RoleBuilderMutateResult, + pub role: RoleKind, + pub factor_source_id: FactorSourceID, + pub validation: sargon::RoleBuilderMutateResult, } -impl From +impl From for FactorSourceValidationStatus { - fn from(val: rules::FactorSourceInRoleBuilderValidationStatus) -> Self { + fn from(val: sargon::FactorSourceInRoleBuilderValidationStatus) -> Self { FactorSourceValidationStatus { - role: val.role, - factor_source_id: val.factor_source_id, + role: val.role.into(), + factor_source_id: val.factor_source_id.into(), validation: val.validation, } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs index 81c8aa5aa..0883645ea 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/mod.rs @@ -1,3 +1,5 @@ mod factor_source_in_role_builder_validation_status; +mod role_kind; pub use factor_source_in_role_builder_validation_status::*; +pub use role_kind::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs new file mode 100644 index 000000000..7f1cb097a --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs @@ -0,0 +1,12 @@ +use crate::prelude::*; +use sargon::RoleKind as InternalRoleKind; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum)] +pub enum RoleKind { + /// The primary role of some matrix of factors + Primary, + /// The recovery role of some matrix of factors + Recovery, + /// The confirmation role of some matrix of factors + Confirmation, +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs new file mode 100644 index 000000000..7a7113b5e --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs @@ -0,0 +1,91 @@ +use crate::prelude::*; + +// use sargon::ConfirmationRoleWithFactorSources as InternalConfirmationRoleWithFactorSources; +// use sargon::PrimaryRoleWithFactorSources as InternalPrimaryRoleWithFactorSources; +// use sargon::RecoveryRoleWithFactorSources as InternalRecoveryRoleWithFactorSources; + +// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] +// pub struct PrimaryRoleWithFactorSources { +// pub threshold: u8, +// pub threshold_factors: Vec, +// pub override_factors: Vec, +// } + +macro_rules! role_conversion { + // Impl From -> crate + (from_internal: $internal:ident, $uniffi:ident) => { + impl From<$internal> for $uniffi { + fn from(value: $internal) -> Self { + Self { + threshold: value.get_threshold(), + threshold_factors: value + .get_threshold_factors() + .into_iter() + .map(|x| x.clone().into()) + .collect(), + override_factors: value + .get_override_factors() + .into_iter() + .map(|x| x.clone().into()) + .collect(), + } + } + } + }; + + // Impl From -> Internal + (to_internal: $internal_factor:ty => $uniffi:ident, $internal:ident) => { + impl From<$uniffi> for $internal { + fn from(value: $uniffi) -> Self { + unsafe { + Self::unbuilt_with_factors( + value.threshold, + value.threshold_factors.into_iter().map(|x| Into::<$internal_factor>::into(x.clone())).collect::>(), + value.override_factors.into_iter().map(|x| Into::<$internal_factor>::into(x.clone())).collect::>(), + ) + } + } + } + }; + + (impl_from: $factor_level:ty => $uniffi_name:ident => $internal_name:ident ) => { + role_conversion!( + from_internal: $internal_name, $uniffi_name + ); + role_conversion!( + to_internal: $factor_level => $uniffi_name, $internal_name + ); + }; + + (struct: $struct_name:ident, $factor_level:ty) => { + #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] + pub struct $struct_name { + pub threshold: u8, + pub threshold_factors: Vec<$factor_level>, + pub override_factors: Vec<$factor_level>, + } + paste! { + use sargon::$struct_name as [< Internal $struct_name>]; + role_conversion!( + impl_from: [< Internal $factor_level>] => $struct_name => [< Internal $struct_name >] + ); + } + }; + (for: $role:ident $factor_level:ty) => { + paste! { + role_conversion!( + struct: [< $role RoleWith $factor_level s >], + $factor_level + ); + } + }; + ($factor_level:ty) => { + paste! { + use sargon::$factor_level as [< Internal $factor_level>]; + } + role_conversion!(for: Primary $factor_level); + role_conversion!(for: Recovery $factor_level); + role_conversion!(for: Confirmation $factor_level); + }; +} +pub(crate) use role_conversion; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs new file mode 100644 index 000000000..28cd6e0f3 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs @@ -0,0 +1,8 @@ +mod decl_role_macro; +mod roles_factor_sources; +mod roles_factor_instances; +mod roles_factor_source_ids; + +pub use roles_factor_sources::*; +pub use roles_factor_instances::*; +pub use roles_factor_source_ids::*; \ No newline at end of file diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs new file mode 100644 index 000000000..0eacbffe6 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs @@ -0,0 +1,5 @@ +use crate::prelude::*; + +use super::decl_role_macro::role_conversion; + +role_conversion!(FactorInstance); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs new file mode 100644 index 000000000..b1fc38fc5 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs @@ -0,0 +1,5 @@ +use crate::prelude::*; + +use super::decl_role_macro::role_conversion; + +role_conversion!(FactorSourceID); \ No newline at end of file diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs new file mode 100644 index 000000000..21fd7a391 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs @@ -0,0 +1,5 @@ +use crate::prelude::*; + +use super::decl_role_macro::role_conversion; + +role_conversion!(FactorSource); \ No newline at end of file diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/mod.rs new file mode 100644 index 000000000..073ee4839 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/mod.rs @@ -0,0 +1,7 @@ +mod security_structure_of_factor_instances; +mod security_structure_of_factor_source_ids; +mod security_structure_of_factor_sources; + +pub use security_structure_of_factor_instances::*; +pub use security_structure_of_factor_source_ids::*; +pub use security_structure_of_factor_sources::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs similarity index 56% rename from crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs rename to crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs index 6983fa330..a85f69cf3 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs @@ -13,3 +13,17 @@ pub struct SecurityStructureOfFactorInstances { /// and Confirmation role. pub matrix_of_factors: MatrixOfFactorInstances, } + + +use sargon::SecurityStructureOfFactorSourceIDs as InternalSecurityStructureOfFactorSourceIDs; + +#[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] +pub struct SecurityStructureOfFactorSourceIDs { + /// Metadata of this Security Structure, such as globally unique and + /// stable identifier, creation date and user chosen label (name). + pub metadata: SecurityStructureMetadata, + + /// The structure of factors to use for certain roles, Primary, Recovery + /// and Confirmation role. + pub matrix_of_factors: MatrixOfFactorSourceIds, +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs similarity index 84% rename from crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs rename to crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs index 871a05527..2b4e95f94 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs @@ -1,5 +1,7 @@ use crate::prelude::*; +use sargon::SecurityStructureOfFactorSourceIDs as InternalSecurityStructureOfFactorSourceIDs; + decl_vec_samples_for!( SecurityStructuresOfFactorSourceIDs, SecurityStructureOfFactorSourceIDs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs similarity index 52% rename from crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs rename to crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs index 42c3d70e1..a7e38cca4 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_of_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs @@ -1,5 +1,19 @@ use crate::prelude::*; +use sargon::MatrixOfFactorSources as InternalMatrixOfFactorSources; +use sargon::SecurityStructureOfFactorSources as InternalSecurityStructureOfFactorSources; + +#[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] +pub struct SecurityStructureOfFactorSources { + /// Metadata of this Security Structure, such as globally unique and + /// stable identifier, creation date and user chosen label (name). + pub metadata: SecurityStructureMetadata, + + /// The structure of factors to use for certain roles, Primary, Recovery + /// and Confirmation role. + pub matrix_of_factors: MatrixOfFactorSources, +} + #[uniffi::export] pub fn new_security_structure_of_factor_sources_sample( ) -> SecurityStructureOfFactorSources { @@ -12,20 +26,6 @@ pub fn new_security_structure_of_factor_sources_sample_other( InternalSecurityStructureOfFactorSources::sample_other().into() } -#[uniffi::export] -pub fn new_security_structure_of_factor_sources_auto_in_days( - metadata: SecurityStructureMetadata, - number_of_days_until_auto_confirmation: u16, - matrix_of_factors: MatrixOfFactorSources, -) -> SecurityStructureOfFactorSources { - InternalSecurityStructureOfFactorSources::new_with_days( - metadata.into_internal(), - number_of_days_until_auto_confirmation, - matrix_of_factors.into_internal(), - ) - .into() -} - #[uniffi::export] pub fn new_matrix_of_factor_sources_sample() -> MatrixOfFactorSources { InternalMatrixOfFactorSources::sample().into() diff --git a/crates/sargon/Cargo.toml b/crates/sargon/Cargo.toml index f7d675a15..711fe419d 100644 --- a/crates/sargon/Cargo.toml +++ b/crates/sargon/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sargon" # Don't forget to update version in crates/sargon-uniffi/Cargo.toml -version = "1.1.70" +version = "1.1.71" edition = "2021" build = "build.rs" diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs index 1eb0cff8d..0918d7ff4 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/factor_source_id_samples.rs @@ -1,51 +1,51 @@ use crate::prelude::*; impl FactorSourceID { - pub(crate) fn sample_device() -> Self { + pub fn sample_device() -> Self { FactorSourceIDFromHash::sample_device().into() } - pub(crate) fn sample_ledger() -> Self { + pub fn sample_ledger() -> Self { FactorSourceIDFromHash::sample_ledger().into() } - pub(crate) fn sample_ledger_other() -> Self { + pub fn sample_ledger_other() -> Self { FactorSourceIDFromHash::sample_ledger_other().into() } - pub(crate) fn sample_arculus() -> Self { + pub fn sample_arculus() -> Self { FactorSourceIDFromHash::sample_arculus().into() } - pub(crate) fn sample_arculus_other() -> Self { + pub fn sample_arculus_other() -> Self { FactorSourceIDFromHash::sample_arculus_other().into() } - pub(crate) fn sample_password() -> Self { + pub fn sample_password() -> Self { FactorSourceIDFromHash::sample_password().into() } - pub(crate) fn sample_password_other() -> Self { + pub fn sample_password_other() -> Self { FactorSourceIDFromHash::sample_password_other().into() } /// Radix Wallet (UI) calls this "passphrase" - pub(crate) fn sample_off_device() -> Self { + pub fn sample_off_device() -> Self { FactorSourceIDFromHash::sample_off_device().into() } /// Radix Wallet (UI) calls this "passphrase" - pub(crate) fn sample_off_device_other() -> Self { + pub fn sample_off_device_other() -> Self { FactorSourceIDFromHash::sample_off_device_other().into() } - pub(crate) fn sample_security_questions() -> Self { + pub fn sample_security_questions() -> Self { FactorSourceIDFromHash::sample_security_questions().into() } - pub(crate) fn sample_device_other() -> Self { + pub fn sample_device_other() -> Self { FactorSourceIDFromHash::sample_device_other().into() } - pub(crate) fn sample_security_questions_other() -> Self { + pub fn sample_security_questions_other() -> Self { FactorSourceIDFromHash::sample_security_questions_other().into() } - pub(crate) fn sample_trusted_contact() -> Self { + pub fn sample_trusted_contact() -> Self { FactorSource::sample_trusted_contact_frank().id() } - pub(crate) fn sample_trusted_contact_other() -> Self { + pub fn sample_trusted_contact_other() -> Self { FactorSource::sample_trusted_contact_grace().id() } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs index 39708a983..e5e1338a6 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/mod.rs @@ -8,7 +8,7 @@ mod matrix_of_factor_sources; pub(crate) use abstract_matrix_builder_or_built::*; #[allow(unused_imports)] pub use builder::*; -pub(crate) use factor_source_id_samples::*; +pub use factor_source_id_samples::*; pub use matrix_of_factor_instances::*; pub use matrix_of_factor_source_ids::*; pub use matrix_of_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs index f27db513c..f896a7227 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -28,7 +28,7 @@ impl RoleKind::from_u8(R).expect("RoleKind should be valid") } - pub(crate) fn with_factors( + pub unsafe fn unbuilt_with_factors( threshold: u8, threshold_factors: impl IntoIterator, override_factors: impl IntoIterator, @@ -66,6 +66,16 @@ impl override_factors, } } + + pub(crate) fn with_factors( + threshold: u8, + threshold_factors: impl IntoIterator, + override_factors: impl IntoIterator, + ) -> Self { + unsafe { + Self::unbuilt_with_factors(threshold, threshold_factors, override_factors) + } + } } impl AbstractRoleBuilderOrBuilt { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs index 179d18f1c..a75b087f2 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub(crate) type ConfirmationRoleWithFactorInstances = +pub type ConfirmationRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_CONFIRMATION }>; impl HasSampleValues for ConfirmationRoleWithFactorInstances { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs index ca98a01d5..bb3e03464 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/mod.rs @@ -4,8 +4,8 @@ mod primary_role_with_factor_instances; mod recovery_role_with_factor_instances; mod role_with_factor_instances; -pub(crate) use confirmation_role_with_factor_instances::*; +pub use confirmation_role_with_factor_instances::*; pub use general_role_with_hierarchical_deterministic_factor_instances::*; -pub(crate) use primary_role_with_factor_instances::*; -pub(crate) use recovery_role_with_factor_instances::*; -pub(crate) use role_with_factor_instances::*; +pub use primary_role_with_factor_instances::*; +pub use recovery_role_with_factor_instances::*; +pub use role_with_factor_instances::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs index 16bfec6ae..d2f5fb042 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub(crate) type PrimaryRoleWithFactorInstances = +pub type PrimaryRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_PRIMARY }>; impl HasSampleValues for PrimaryRoleWithFactorInstances { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs index 02795e935..7956f523f 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub(crate) type RecoveryRoleWithFactorInstances = +pub type RecoveryRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_RECOVERY }>; impl HasSampleValues for RecoveryRoleWithFactorInstances { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs index 38b69a1ec..74c7c3d29 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs @@ -1,7 +1,8 @@ use crate::prelude::*; -pub type ConfirmationRoleWithFactorSourceIds = +pub type ConfirmationRoleWithFactorSourceIDs = RoleWithFactorSourceIds<{ ROLE_CONFIRMATION }>; +pub type ConfirmationRoleWithFactorSourceIds = ConfirmationRoleWithFactorSourceIDs; impl HasSampleValues for ConfirmationRoleWithFactorSourceIds { /// Config MFA 1.1 diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs index 1589cb921..a4b2974f5 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs @@ -1,7 +1,8 @@ use crate::prelude::*; -pub type PrimaryRoleWithFactorSourceIds = +pub type PrimaryRoleWithFactorSourceIDs = RoleWithFactorSourceIds<{ ROLE_PRIMARY }>; +pub type PrimaryRoleWithFactorSourceIds = PrimaryRoleWithFactorSourceIDs; impl PrimaryRoleWithFactorSourceIds { /// Config MFA 1.1 diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs index 26edb425d..040e021b0 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs @@ -1,7 +1,8 @@ use crate::prelude::*; -pub type RecoveryRoleWithFactorSourceIds = +pub type RecoveryRoleWithFactorSourceIDs = RoleWithFactorSourceIds<{ ROLE_RECOVERY }>; +pub type RecoveryRoleWithFactorSourceIds = RecoveryRoleWithFactorSourceIDs; #[cfg(test)] mod tests { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs index 4cd2f1105..54524c7f5 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs @@ -17,9 +17,9 @@ pub struct FactorSourceTemplate { pub(crate) type RoleTemplate = AbstractBuiltRoleWithFactor; -pub(crate) type PrimaryRoleTemplate = RoleTemplate<{ ROLE_PRIMARY }>; -pub(crate) type RecoveryRoleTemplate = RoleTemplate<{ ROLE_RECOVERY }>; -pub(crate) type ConfirmationRoleTemplate = RoleTemplate<{ ROLE_CONFIRMATION }>; +pub type PrimaryRoleTemplate = RoleTemplate<{ ROLE_PRIMARY }>; +pub type RecoveryRoleTemplate = RoleTemplate<{ ROLE_RECOVERY }>; +pub type ConfirmationRoleTemplate = RoleTemplate<{ ROLE_CONFIRMATION }>; impl PrimaryRoleTemplate { pub(crate) fn new( diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs index f69ea6a12..56c8e4665 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub(crate) type ConfirmationRoleWithFactorSources = +pub type ConfirmationRoleWithFactorSources = RoleWithFactorSources<{ ROLE_CONFIRMATION }>; impl HasSampleValues for ConfirmationRoleWithFactorSources { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs index b3012f2b2..b629e2284 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/mod.rs @@ -3,7 +3,7 @@ mod primary_role_with_factor_sources; mod recovery_role_with_factor_sources; mod roles_with_factor_sources; -pub(crate) use confirmation_role_with_factor_sources::*; -pub(crate) use primary_role_with_factor_sources::*; -pub(crate) use recovery_role_with_factor_sources::*; -pub(crate) use roles_with_factor_sources::*; +pub use confirmation_role_with_factor_sources::*; +pub use primary_role_with_factor_sources::*; +pub use recovery_role_with_factor_sources::*; +pub use roles_with_factor_sources::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs index 5801e689e..e7fe56eca 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs @@ -1,7 +1,6 @@ use crate::prelude::*; -pub(crate) type PrimaryRoleWithFactorSources = - RoleWithFactorSources<{ ROLE_PRIMARY }>; +pub type PrimaryRoleWithFactorSources = RoleWithFactorSources<{ ROLE_PRIMARY }>; impl HasSampleValues for PrimaryRoleWithFactorSources { fn sample() -> Self { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs index 6482c4dc2..75949db79 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub(crate) type RecoveryRoleWithFactorSources = +pub type RecoveryRoleWithFactorSources = RoleWithFactorSources<{ ROLE_RECOVERY }>; impl HasSampleValues for RecoveryRoleWithFactorSources { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs index 4e39fbee0..818275a84 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/mod.rs @@ -6,4 +6,4 @@ mod factor_source_level; pub use factor_instance_level::*; pub use factor_source_id_level::*; pub use factor_source_kind_level::*; -pub(crate) use factor_source_level::*; +pub use factor_source_level::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs index c99cb3972..dcd18772f 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/mod.rs @@ -2,6 +2,6 @@ mod abstract_role_builder_or_built; mod builder; mod factor_levels; -pub(crate) use abstract_role_builder_or_built::*; +pub use abstract_role_builder_or_built::*; pub use builder::*; pub use factor_levels::*; From 93f9e55200d510a60267d8e01a51882f3c9f3272 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 11:08:53 +0100 Subject: [PATCH 05/70] [no ci] WIP --- .../roles/decl_role_macro.rs | 102 ++++++++++++++---- .../mfa/security_structures/roles/mod.rs | 2 + .../abstract_matrix_builder_or_built.rs | 14 +++ .../roles/abstract_role_builder_or_built.rs | 46 +++++++- 4 files changed, 140 insertions(+), 24 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs index 7a7113b5e..589c2fc7b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs @@ -1,17 +1,43 @@ use crate::prelude::*; -// use sargon::ConfirmationRoleWithFactorSources as InternalConfirmationRoleWithFactorSources; -// use sargon::PrimaryRoleWithFactorSources as InternalPrimaryRoleWithFactorSources; -// use sargon::RecoveryRoleWithFactorSources as InternalRecoveryRoleWithFactorSources; - -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct PrimaryRoleWithFactorSources { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, +// This macro generates "Role" types, for each RoleKind: Primary, Recovery, Confirmation +// for the specified "Factor Level", so input is `FactorSource` or `FactorInstance` +// or `FactorSourceID` etc. +// It will generate the following: +// struct PrimaryRoleWithFactor<$FACTOR_LEVEL> { +// threshold: u8, +// threshold_factors: Vec<$FACTOR_LEVEL>, +// override_factors: Vec<$FACTOR_LEVEL>, // } - +// and `struct RecoveryRoleWithFactor<$FACTOR_LEVEL>` +// and `struct ConfirmationRoleWithFactor<$FACTOR_LEVEL>` +// +// And it will generate: +// * `From> for PrimaryRoleWithFactor<$FACTOR_LEVEL>` +// * `From> for RecoveryRoleWithFactor<$FACTOR_LEVEL>` +// * `From> for ConfirmationRoleWithFactor<$FACTOR_LEVEL>` +// +// And analogously it will impl the inverse conversion: +// * `From> for InternalPrimaryRoleWithFactor<$FACTOR_LEVEL>` +// * `From> for InternalRecoveryRoleWithFactor<$FACTOR_LEVEL>` +// * `From> for InternalConfirmationRoleWithFactor<$FACTOR_LEVEL>` +// +// Furthermore it will generate `HasSampleValues` impl for each of the generated structs. +// and also uniffi export them. macro_rules! role_conversion { + ($factor_level:ty) => { + paste! { + use sargon::$factor_level as [< Internal $factor_level>]; + } + role_conversion_inner!(for: Primary $factor_level); + role_conversion_inner!(for: Recovery $factor_level); + role_conversion_inner!(for: Confirmation $factor_level); + }; +} + +pub(crate) use role_conversion; + +macro_rules! role_conversion_inner { // Impl From -> crate (from_internal: $internal:ident, $uniffi:ident) => { impl From<$internal> for $uniffi { @@ -48,44 +74,74 @@ macro_rules! role_conversion { } }; + // Impl `From for X` and `From for InternalX` + // and impl `HasSampleValues` for X` and UniFFI export `new_X_sample` and `new_X_sample_other`. (impl_from: $factor_level:ty => $uniffi_name:ident => $internal_name:ident ) => { - role_conversion!( + role_conversion_inner!( from_internal: $internal_name, $uniffi_name ); - role_conversion!( + role_conversion_inner!( to_internal: $factor_level => $uniffi_name, $internal_name ); + + impl HasSampleValues for $uniffi_name { + fn sample() -> Self { + $internal_name::sample().into() + } + fn sample_other() -> Self { + $internal_name::sample().into() + } + } + + paste! { + #[uniffi::export] + pub fn [< new_ $uniffi_name:snake _ sample >]() -> $uniffi_name { + $uniffi_name::sample() + } + + #[uniffi::export] + pub fn [< new_ $uniffi_name:snake _ sample_other >]() -> $uniffi_name { + $uniffi_name::sample_other() + } + } }; + // Declare the struct and impl `From + // and by recursively calling `role_conversion_inner` also impl + // `From` conversions. (struct: $struct_name:ident, $factor_level:ty) => { + /// A role with a threshold, threshold_factors and override_factors. #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] pub struct $struct_name { + + /// How many threshold factors that must be used to perform some function with + /// this role. pub threshold: u8, + + /// Factors which are used in combination with other factors, amounting to at + /// least `threshold` many factors to perform some function with this role. pub threshold_factors: Vec<$factor_level>, + + /// Overriding / Super admin / "sudo" / God / factors, **ANY** + /// single of these factor which can perform the function of this role, + /// disregarding of `threshold`. pub override_factors: Vec<$factor_level>, } paste! { use sargon::$struct_name as [< Internal $struct_name>]; - role_conversion!( + role_conversion_inner!( impl_from: [< Internal $factor_level>] => $struct_name => [< Internal $struct_name >] ); } }; (for: $role:ident $factor_level:ty) => { paste! { - role_conversion!( + role_conversion_inner!( struct: [< $role RoleWith $factor_level s >], $factor_level ); } }; - ($factor_level:ty) => { - paste! { - use sargon::$factor_level as [< Internal $factor_level>]; - } - role_conversion!(for: Primary $factor_level); - role_conversion!(for: Recovery $factor_level); - role_conversion!(for: Confirmation $factor_level); - }; } -pub(crate) use role_conversion; + +pub(crate) use role_conversion_inner; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs index 28cd6e0f3..2f06f25d5 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs @@ -1,4 +1,6 @@ +#[macro_use] mod decl_role_macro; + mod roles_factor_sources; mod roles_factor_instances; mod roles_factor_source_ids; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs index f2d7b2779..8795c52bc 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs @@ -1,5 +1,18 @@ use crate::prelude::*; + +/// Either a matrix or a **builder of a matrix** with a Primary, Recovery and Confirmation +/// role or **builder of roles**. +/// This type is shared by: +/// * MatrixBuilder (FactorSourceID) +/// +/// # Built +/// * MatrixOfFactorSources +/// * MatrixOfFactorSourceIds +/// * MatrixOfFactorInstances +/// +/// For "built types" the `built` field is `PhantomData<()>`, for the `MatrixBuilder` +/// it is `PhantomData`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AbstractMatrixBuilderOrBuilt { @@ -15,6 +28,7 @@ pub struct AbstractMatrixBuilderOrBuilt { pub(crate) number_of_days_until_auto_confirm: u16, } + impl AbstractMatrixBuilderOrBuilt { pub const DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM: u16 = 14; } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs index f896a7227..3e54fec6f 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -4,6 +4,29 @@ use serde::{Deserialize, Serialize}; use crate::prelude::*; +/// Either a role or a **builder of a role** with a threshold, threshold_factors and override_factors. +/// This type is shared by: +/// # Builder +/// * PrimaryRoleBuilder (FactorSourceID) +/// * RecoveryRoleBuilder (FactorSourceID) +/// * ConfirmationRoleBuilder (FactorSourceID) +/// +/// # Built +/// +/// ## FactorSourceID +/// * PrimaryRoleWithFactorSourceID +/// * RecoveryRoleWithFactorSourceID +/// * ConfirmationRoleWithFactorSourceID +/// +/// ## FactorSource +/// * PrimaryRoleWithFactorSource +/// * RecoveryRoleWithFactorSource +/// * ConfirmationRoleWithFactorSource +/// +/// ## FactorInstance +/// * PrimaryRoleWithFactorInstances +/// * RecoveryRoleWithFactorInstances +/// * ConfirmationRoleWithFactorInstances #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AbstractRoleBuilderOrBuilt { @@ -11,8 +34,17 @@ pub struct AbstractRoleBuilderOrBuilt { #[doc(hidden)] built: PhantomData, + /// How many threshold factors that must be used to perform some function with + /// this role. threshold: u8, + + /// Factors which are used in combination with other factors, amounting to at + /// least `threshold` many factors to perform some function with this role. threshold_factors: Vec, + + /// Overriding / Super admin / "sudo" / God / factors, **ANY** + /// single of these factor which can perform the function of this role, + /// disregarding of `threshold`. override_factors: Vec, } @@ -73,12 +105,17 @@ impl override_factors: impl IntoIterator, ) -> Self { unsafe { - Self::unbuilt_with_factors(threshold, threshold_factors, override_factors) + Self::unbuilt_with_factors( + threshold, + threshold_factors, + override_factors, + ) } } } impl AbstractRoleBuilderOrBuilt { + /// Threshold and Override factors mixed (threshold first). pub fn all_factors(&self) -> Vec<&F> { self.threshold_factors .iter() @@ -86,14 +123,21 @@ impl AbstractRoleBuilderOrBuilt { .collect() } + /// Factors which are used in combination with other factors, amounting to at + /// least `threshold` many factors to perform some function with this role. pub fn get_threshold_factors(&self) -> &Vec { &self.threshold_factors } + /// Overriding / Super admin / "sudo" / God / factors, **ANY** + /// single of these factor which can perform the function of this role, + /// disregarding of `threshold`. pub fn get_override_factors(&self) -> &Vec { &self.override_factors } + /// How many threshold factors that must be used to perform some function with + /// this role. pub fn get_threshold(&self) -> u8 { self.threshold } From 698277e02fc9786d1016579d83bb4a598b02f97b Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 11:17:24 +0100 Subject: [PATCH 06/70] [no ci] WIP --- .../matrices/decl_matrix_macro.rs | 28 +++++++++++++++++ .../matrices/matrix_of_factor_source_ids.rs | 9 +++++- .../mfa/security_structures/matrices/mod.rs | 3 ++ .../matrices/matrix_of_factor_source_ids.rs | 30 +++++++++++-------- .../security_structure_of_factor_sources.rs | 16 ++++++---- 5 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs new file mode 100644 index 000000000..78bb4fed0 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs @@ -0,0 +1,28 @@ +use crate::prelude::*; + +macro_rules! matrix_conversion { + (struct: $struct_name:ident, $factor_level:ty) => { + paste! { + #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] + pub struct $struct_name { + pub primary_role: [< PrimaryRoleWith $factor_level s >], + pub recovery_role: [< RecoveryRoleWith $factor_level s >], + pub confirmation_role: [< ConfirmationRoleWith $factor_level s >], + + pub number_of_days_until_auto_confirm: u16, + } + } + }; + ($factor_level:ty) => { + paste! { + use sargon::$factor_level as [< Internal $factor_level>]; + matrix_conversion!( + struct: [< $role Of $factor_level s >], + $factor_level + ); + } + }; +} + +pub(crate) use matrix_conversion; + diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 18c28bb0c..6270e4e0a 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -87,6 +87,13 @@ impl From for MatrixOfFactorSourceIds { } impl From for InternalMatrixOfFactorSourceIds { fn from(value: MatrixOfFactorSourceIds) -> Self { - todo!() + unsafe { + Self::unbuilt_with_roles_and_days( + value.primary_role.into(), + value.recovery_role.into(), + value.confirmation_role.into(), + value.number_of_days_until_auto_confirm, + ) + } } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs index 99e061a89..d38c5eb12 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/mod.rs @@ -1,3 +1,6 @@ +#[macro_use] +mod decl_matrix_macro; + mod matrix_of_factor_instances; mod matrix_of_factor_source_ids; mod matrix_of_factor_sources; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index f5f7372dd..d84632882 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -3,7 +3,7 @@ use crate::prelude::*; pub type MatrixOfFactorSourceIds = AbstractMatrixBuilt; impl MatrixOfFactorSourceIds { - pub(crate) fn _unvalidated_with_roles_and_days( + pub unsafe fn unbuilt_with_roles_and_days( primary: PrimaryRoleWithFactorSourceIds, recovery: RecoveryRoleWithFactorSourceIds, confirmation: ConfirmationRoleWithFactorSourceIds, @@ -26,12 +26,14 @@ impl MatrixOfFactorSourceIds { recovery: RecoveryRoleWithFactorSourceIds, confirmation: ConfirmationRoleWithFactorSourceIds, ) -> Self { - Self::_unvalidated_with_roles_and_days( - primary, - recovery, - confirmation, - Self::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, - ) + unsafe { + Self::unbuilt_with_roles_and_days( + primary, + recovery, + confirmation, + Self::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + ) + } } } @@ -43,12 +45,14 @@ impl MatrixOfFactorSourceIds { confirmation: ConfirmationRoleWithFactorSourceIds, number_of_days_until_auto_confirm: u16, ) -> Self { - Self::_unvalidated_with_roles_and_days( - primary, - recovery, - confirmation, - number_of_days_until_auto_confirm, - ) + unsafe { + Self::unbuilt_with_roles_and_days( + primary, + recovery, + confirmation, + number_of_days_until_auto_confirm, + ) + } } pub(crate) fn with_roles( diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs index 0932e096d..6fe37ccce 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs @@ -78,12 +78,16 @@ impl From> impl From for MatrixOfFactorSourceIDs { fn from(value: MatrixOfFactorSources) -> Self { - Self::_unvalidated_with_roles_and_days( - PrimaryRoleWithFactorSourceIds::from(value.primary_role), - RecoveryRoleWithFactorSourceIds::from(value.recovery_role), - ConfirmationRoleWithFactorSourceIds::from(value.confirmation_role), - value.number_of_days_until_auto_confirm, - ) + unsafe { + Self::unbuilt_with_roles_and_days( + PrimaryRoleWithFactorSourceIds::from(value.primary_role), + RecoveryRoleWithFactorSourceIds::from(value.recovery_role), + ConfirmationRoleWithFactorSourceIds::from( + value.confirmation_role, + ), + value.number_of_days_until_auto_confirm, + ) + } } } From 20201006ca9029f8e81aecd20c458e12031ba75a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 11:20:18 +0100 Subject: [PATCH 07/70] [no ci] WIP --- .../matrices/matrix_of_factor_source_ids.rs | 8 +++++++- .../matrices/abstract_matrix_builder_or_built.rs | 5 ++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 6270e4e0a..659c8d0ca 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -82,7 +82,13 @@ pub struct MatrixOfFactorSourceIds { impl From for MatrixOfFactorSourceIds { fn from(value: InternalMatrixOfFactorSourceIds) -> Self { - todo!() + Self { + primary_role: value.primary().clone().into(), + recovery_role: value.recovery().clone().into(), + confirmation_role: value.confirmation().clone().into(), + number_of_days_until_auto_confirm: value + .number_of_days_until_auto_confirm, + } } } impl From for InternalMatrixOfFactorSourceIds { diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs index 8795c52bc..679378cdd 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs @@ -1,11 +1,10 @@ use crate::prelude::*; - /// Either a matrix or a **builder of a matrix** with a Primary, Recovery and Confirmation /// role or **builder of roles**. /// This type is shared by: /// * MatrixBuilder (FactorSourceID) -/// +/// /// # Built /// * MatrixOfFactorSources /// * MatrixOfFactorSourceIds @@ -26,7 +25,7 @@ pub struct AbstractMatrixBuilderOrBuilt { pub(crate) confirmation_role: AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, F, U>, - pub(crate) number_of_days_until_auto_confirm: u16, + pub number_of_days_until_auto_confirm: u16, } impl AbstractMatrixBuilderOrBuilt { From 562fae8458cd5d4f9322dc227506ca6cd1e5b15b Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 11:41:00 +0100 Subject: [PATCH 08/70] [no ci] WIP --- .../mfa/security_structures/builder.rs | 4 +- .../security_structures/error_conversion.rs | 4 +- .../matrices/decl_matrix_macro.rs | 71 +++++++++++- .../matrices/matrix_of_factor_instances.rs | 91 +-------------- .../matrices/matrix_of_factor_source_ids.rs | 104 +----------------- .../matrices/matrix_of_factor_sources.rs | 23 +--- .../security_structures/models/role_kind.rs | 16 +-- .../mfa/security_structures/roles/mod.rs | 6 +- .../roles/roles_factor_source_ids.rs | 2 +- .../roles/roles_factor_sources.rs | 2 +- .../security_structure_of_factor_instances.rs | 5 +- .../security_structure_of_factor_sources.rs | 10 -- .../abstract_matrix_builder_or_built.rs | 19 ++++ .../matrices/matrix_of_factor_source_ids.rs | 18 --- .../roles/abstract_role_builder_or_built.rs | 12 +- ...onfirmation_role_with_factor_source_ids.rs | 3 +- 16 files changed, 124 insertions(+), 266 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs index 2a5804d74..ecf08bd6c 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -101,7 +101,7 @@ impl SecurityShieldBuilder { let factors = access(builder); factors .iter() - .map(|x| crate::FactorSourceID::from(x.clone())) + .map(|x| crate::FactorSourceID::from(*x)) .collect::>() }) } @@ -339,7 +339,7 @@ impl SecurityShieldBuilder { impl FactorSourceID { pub fn new(inner: impl Borrow) -> Self { - Self::from(inner.borrow().clone()) + Self::from(*inner.borrow()) } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs index 6255b5c99..1100dc906 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs @@ -3,14 +3,14 @@ use sargon::{MatrixBuilderValidation, RoleBuilderValidation}; use crate::prelude::*; impl From for CommonError { - fn from(val: MatrixBuilderValidation) -> Self { + fn from(_value: MatrixBuilderValidation) -> Self { // CommonError::BuildError(format!("{:?}", val)) CommonError::Unknown } } impl From for CommonError { - fn from(val: RoleBuilderValidation) -> Self { + fn from(_value: RoleBuilderValidation) -> Self { // CommonError::BuildError(format!("{:?}", val)) CommonError::Unknown } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs index 78bb4fed0..2ff7920c7 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs @@ -1,6 +1,67 @@ use crate::prelude::*; macro_rules! matrix_conversion { + // Impl From -> crate + (from_internal: $internal:ident, $uniffi:ident) => { + impl From<$internal> for $uniffi { + fn from(value: $internal) -> Self { + Self { + primary_role: value.primary().clone().into(), + recovery_role: value.recovery().clone().into(), + confirmation_role: value.confirmation().clone().into(), + number_of_days_until_auto_confirm: value + .number_of_days_until_auto_confirm, + } + } + } + }; + + // Impl From -> Internal + (to_internal: $internal_factor:ty => $uniffi:ident, $internal:ident) => { + impl From<$uniffi> for $internal { + fn from(value: $uniffi) -> Self { + unsafe { + Self::unbuilt_with_roles_and_days( + value.primary_role.into(), + value.recovery_role.into(), + value.confirmation_role.into(), + value.number_of_days_until_auto_confirm, + ) + } + } + } + }; + // Impl `From for X` and `From for InternalX` + // and impl `HasSampleValues` for X` and UniFFI export `new_X_sample` and `new_X_sample_other`. + (impl_from: $factor_level:ty => $uniffi_name:ident => $internal_name:ident ) => { + matrix_conversion!( + from_internal: $internal_name, $uniffi_name + ); + matrix_conversion!( + to_internal: $factor_level => $uniffi_name, $internal_name + ); + + impl HasSampleValues for $uniffi_name { + fn sample() -> Self { + $internal_name::sample().into() + } + fn sample_other() -> Self { + $internal_name::sample().into() + } + } + + paste! { + #[uniffi::export] + pub fn [< new_ $uniffi_name:snake _ sample >]() -> $uniffi_name { + $uniffi_name::sample() + } + + #[uniffi::export] + pub fn [< new_ $uniffi_name:snake _ sample_other >]() -> $uniffi_name { + $uniffi_name::sample_other() + } + } + }; (struct: $struct_name:ident, $factor_level:ty) => { paste! { #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] @@ -8,16 +69,21 @@ macro_rules! matrix_conversion { pub primary_role: [< PrimaryRoleWith $factor_level s >], pub recovery_role: [< RecoveryRoleWith $factor_level s >], pub confirmation_role: [< ConfirmationRoleWith $factor_level s >], - + pub number_of_days_until_auto_confirm: u16, } + + use sargon::$struct_name as [< Internal $struct_name>]; + matrix_conversion!( + impl_from: [< Internal $factor_level>] => $struct_name => [< Internal $struct_name >] + ); } }; ($factor_level:ty) => { paste! { use sargon::$factor_level as [< Internal $factor_level>]; matrix_conversion!( - struct: [< $role Of $factor_level s >], + struct: [< MatrixOf $factor_level s >], $factor_level ); } @@ -25,4 +91,3 @@ macro_rules! matrix_conversion { } pub(crate) use matrix_conversion; - diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs index 4a32536d1..e03790b19 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs @@ -1,92 +1,5 @@ use crate::prelude::*; -use sargon::ConfirmationRoleWithFactorInstances as InternalConfirmationRoleWithFactorInstances; -use sargon::MatrixOfFactorInstances as InternalMatrixOfFactorInstances; -use sargon::PrimaryRoleWithFactorInstances as InternalPrimaryRoleWithFactorInstances; -use sargon::RecoveryRoleWithFactorInstances as InternalRecoveryRoleWithFactorInstances; +use super::decl_matrix_macro::matrix_conversion; -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct PrimaryRoleWithFactorInstances { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, -// } - -// impl From -// for PrimaryRoleWithFactorInstances -// { -// fn from(value: InternalPrimaryRoleWithFactorInstances) -> Self { -// todo!() -// } -// } -// impl From -// for InternalPrimaryRoleWithFactorInstances -// { -// fn from(value: PrimaryRoleWithFactorInstances) -> Self { -// todo!() -// } -// } - -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct RecoveryRoleWithFactorInstances { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, -// } - -// impl From -// for RecoveryRoleWithFactorInstances -// { -// fn from(value: InternalRecoveryRoleWithFactorInstances) -> Self { -// todo!() -// } -// } -// impl From -// for InternalRecoveryRoleWithFactorInstances -// { -// fn from(value: RecoveryRoleWithFactorInstances) -> Self { -// todo!() -// } -// } - -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct ConfirmationRoleWithFactorInstances { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, -// } - -// impl From -// for ConfirmationRoleWithFactorInstances -// { -// fn from(value: InternalConfirmationRoleWithFactorInstances) -> Self { -// todo!() -// } -// } -// impl From -// for InternalConfirmationRoleWithFactorInstances -// { -// fn from(value: ConfirmationRoleWithFactorInstances) -> Self { -// todo!() -// } -// } - -#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -pub struct MatrixOfFactorInstances { - pub primary_role: PrimaryRoleWithFactorInstances, - pub recovery_role: RecoveryRoleWithFactorInstances, - pub confirmation_role: ConfirmationRoleWithFactorInstances, - - pub number_of_days_until_auto_confirm: u16, -} - -impl From for MatrixOfFactorInstances { - fn from(value: InternalMatrixOfFactorInstances) -> Self { - todo!() - } -} -impl From for InternalMatrixOfFactorInstances { - fn from(value: MatrixOfFactorInstances) -> Self { - todo!() - } -} +matrix_conversion!(FactorInstance); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 659c8d0ca..1e58f03e6 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -1,105 +1,5 @@ use crate::prelude::*; -use sargon::ConfirmationRoleWithFactorSourceIds as InternalConfirmationRoleWithFactorSourceIds; -use sargon::MatrixOfFactorSourceIds as InternalMatrixOfFactorSourceIds; -use sargon::PrimaryRoleWithFactorSourceIds as InternalPrimaryRoleWithFactorSourceIds; -use sargon::RecoveryRoleWithFactorSourceIds as InternalRecoveryRoleWithFactorSourceIds; +use super::decl_matrix_macro::matrix_conversion; -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct PrimaryRoleWithFactorSourceIds { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, -// } - -// impl From -// for PrimaryRoleWithFactorSourceIds -// { -// fn from(value: InternalPrimaryRoleWithFactorSourceIds) -> Self { -// todo!() -// } -// } -// impl From -// for InternalPrimaryRoleWithFactorSourceIds -// { -// fn from(value: PrimaryRoleWithFactorSourceIds) -> Self { -// todo!() -// } -// } - -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct RecoveryRoleWithFactorSourceIds { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, -// } - -// impl From -// for RecoveryRoleWithFactorSourceIds -// { -// fn from(value: InternalRecoveryRoleWithFactorSourceIds) -> Self { -// todo!() -// } -// } -// impl From -// for InternalRecoveryRoleWithFactorSourceIds -// { -// fn from(value: RecoveryRoleWithFactorSourceIds) -> Self { -// todo!() -// } -// } - -// #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -// pub struct ConfirmationRoleWithFactorSourceIds { -// pub threshold: u8, -// pub threshold_factors: Vec, -// pub override_factors: Vec, -// } - -// impl From -// for ConfirmationRoleWithFactorSourceIds -// { -// fn from(value: InternalConfirmationRoleWithFactorSourceIds) -> Self { -// todo!() -// } -// } -// impl From -// for InternalConfirmationRoleWithFactorSourceIds -// { -// fn from(value: ConfirmationRoleWithFactorSourceIds) -> Self { -// todo!() -// } -// } - -#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -pub struct MatrixOfFactorSourceIds { - pub primary_role: PrimaryRoleWithFactorSourceIDs, - pub recovery_role: RecoveryRoleWithFactorSourceIDs, - pub confirmation_role: ConfirmationRoleWithFactorSourceIDs, - - pub number_of_days_until_auto_confirm: u16, -} - -impl From for MatrixOfFactorSourceIds { - fn from(value: InternalMatrixOfFactorSourceIds) -> Self { - Self { - primary_role: value.primary().clone().into(), - recovery_role: value.recovery().clone().into(), - confirmation_role: value.confirmation().clone().into(), - number_of_days_until_auto_confirm: value - .number_of_days_until_auto_confirm, - } - } -} -impl From for InternalMatrixOfFactorSourceIds { - fn from(value: MatrixOfFactorSourceIds) -> Self { - unsafe { - Self::unbuilt_with_roles_and_days( - value.primary_role.into(), - value.recovery_role.into(), - value.confirmation_role.into(), - value.number_of_days_until_auto_confirm, - ) - } - } -} +matrix_conversion!(FactorSourceID); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs index 48989a7df..44749f199 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs @@ -1,24 +1,5 @@ use crate::prelude::*; -use sargon::MatrixOfFactorSources as InternalMatrixOfFactorSources; +use super::decl_matrix_macro::matrix_conversion; - -#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] -pub struct MatrixOfFactorSources { - pub primary_role: PrimaryRoleWithFactorSources, - pub recovery_role: RecoveryRoleWithFactorSources, - pub confirmation_role: ConfirmationRoleWithFactorSources, - - pub number_of_days_until_auto_confirm: u16, -} - -impl From for MatrixOfFactorSources { - fn from(value: InternalMatrixOfFactorSources) -> Self { - todo!() - } -} -impl From for InternalMatrixOfFactorSources { - fn from(value: MatrixOfFactorSources) -> Self { - todo!() - } -} +matrix_conversion!(FactorSource); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs index 7f1cb097a..6a056b5fd 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs @@ -1,12 +1,14 @@ use crate::prelude::*; use sargon::RoleKind as InternalRoleKind; -#[derive(Clone, Debug, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum)] +#[derive( + Clone, Debug, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum, +)] pub enum RoleKind { - /// The primary role of some matrix of factors - Primary, - /// The recovery role of some matrix of factors - Recovery, - /// The confirmation role of some matrix of factors - Confirmation, + /// The primary role of some matrix of factors + Primary, + /// The recovery role of some matrix of factors + Recovery, + /// The confirmation role of some matrix of factors + Confirmation, } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs index 2f06f25d5..212ff849c 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/mod.rs @@ -1,10 +1,10 @@ #[macro_use] mod decl_role_macro; -mod roles_factor_sources; mod roles_factor_instances; mod roles_factor_source_ids; +mod roles_factor_sources; -pub use roles_factor_sources::*; pub use roles_factor_instances::*; -pub use roles_factor_source_ids::*; \ No newline at end of file +pub use roles_factor_source_ids::*; +pub use roles_factor_sources::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs index b1fc38fc5..6968d506d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs @@ -2,4 +2,4 @@ use crate::prelude::*; use super::decl_role_macro::role_conversion; -role_conversion!(FactorSourceID); \ No newline at end of file +role_conversion!(FactorSourceID); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs index 21fd7a391..96ce677ac 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs @@ -2,4 +2,4 @@ use crate::prelude::*; use super::decl_role_macro::role_conversion; -role_conversion!(FactorSource); \ No newline at end of file +role_conversion!(FactorSource); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs index a85f69cf3..7889cb04c 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs @@ -14,7 +14,6 @@ pub struct SecurityStructureOfFactorInstances { pub matrix_of_factors: MatrixOfFactorInstances, } - use sargon::SecurityStructureOfFactorSourceIDs as InternalSecurityStructureOfFactorSourceIDs; #[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] @@ -25,5 +24,7 @@ pub struct SecurityStructureOfFactorSourceIDs { /// The structure of factors to use for certain roles, Primary, Recovery /// and Confirmation role. - pub matrix_of_factors: MatrixOfFactorSourceIds, + pub matrix_of_factors: MatrixOfFactorSourceIDs, } + +pub type MatrixOfFactorSourceIds = MatrixOfFactorSourceIDs; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs index a7e38cca4..7789cc128 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_sources.rs @@ -25,13 +25,3 @@ pub fn new_security_structure_of_factor_sources_sample_other( ) -> SecurityStructureOfFactorSources { InternalSecurityStructureOfFactorSources::sample_other().into() } - -#[uniffi::export] -pub fn new_matrix_of_factor_sources_sample() -> MatrixOfFactorSources { - InternalMatrixOfFactorSources::sample().into() -} - -#[uniffi::export] -pub fn new_matrix_of_factor_sources_sample_other() -> MatrixOfFactorSources { - InternalMatrixOfFactorSources::sample_other().into() -} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs index 679378cdd..67e911ec3 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs @@ -30,6 +30,25 @@ pub struct AbstractMatrixBuilderOrBuilt { impl AbstractMatrixBuilderOrBuilt { pub const DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM: u16 = 14; + + /// # Safety + /// Rust memory safe, but marked "unsafe" since it might allow for instantiation + /// of unsafe - as in application **unsecure** - MatrixofFactors, which might + /// lead to increase risk for end user to loose funds. + pub unsafe fn unbuilt_with_roles_and_days( + primary: AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, F, U>, + recovery: AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, F, U>, + confirmation: AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, F, U>, + number_of_days_until_auto_confirm: u16, + ) -> Self { + Self { + built: PhantomData, + primary_role: primary, + recovery_role: recovery, + confirmation_role: confirmation, + number_of_days_until_auto_confirm, + } + } } pub type AbstractMatrixBuilt = AbstractMatrixBuilderOrBuilt; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index d84632882..b53c73960 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -3,24 +3,6 @@ use crate::prelude::*; pub type MatrixOfFactorSourceIds = AbstractMatrixBuilt; impl MatrixOfFactorSourceIds { - pub unsafe fn unbuilt_with_roles_and_days( - primary: PrimaryRoleWithFactorSourceIds, - recovery: RecoveryRoleWithFactorSourceIds, - confirmation: ConfirmationRoleWithFactorSourceIds, - number_of_days_until_auto_confirm: u16, - ) -> Self { - assert_eq!(primary.role(), RoleKind::Primary); - assert_eq!(recovery.role(), RoleKind::Recovery); - assert_eq!(confirmation.role(), RoleKind::Confirmation); - Self { - built: PhantomData, - primary_role: primary, - recovery_role: recovery, - confirmation_role: confirmation, - number_of_days_until_auto_confirm, - } - } - pub(crate) fn _unvalidated_with_roles( primary: PrimaryRoleWithFactorSourceIds, recovery: RecoveryRoleWithFactorSourceIds, diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs index 3e54fec6f..815dc6fa4 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -10,19 +10,19 @@ use crate::prelude::*; /// * PrimaryRoleBuilder (FactorSourceID) /// * RecoveryRoleBuilder (FactorSourceID) /// * ConfirmationRoleBuilder (FactorSourceID) -/// +/// /// # Built -/// +/// /// ## FactorSourceID /// * PrimaryRoleWithFactorSourceID /// * RecoveryRoleWithFactorSourceID /// * ConfirmationRoleWithFactorSourceID -/// +/// /// ## FactorSource /// * PrimaryRoleWithFactorSource /// * RecoveryRoleWithFactorSource /// * ConfirmationRoleWithFactorSource -/// +/// /// ## FactorInstance /// * PrimaryRoleWithFactorInstances /// * RecoveryRoleWithFactorInstances @@ -60,6 +60,10 @@ impl RoleKind::from_u8(R).expect("RoleKind should be valid") } + /// # Safety + /// Rust memory safe, but marked "unsafe" since it might allow for instantiation + /// of unsafe - as in application **unsecure** - Role of Factors, which might + /// lead to increase risk for end user to loose funds. pub unsafe fn unbuilt_with_factors( threshold: u8, threshold_factors: impl IntoIterator, diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs index 74c7c3d29..e62f29505 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs @@ -2,7 +2,8 @@ use crate::prelude::*; pub type ConfirmationRoleWithFactorSourceIDs = RoleWithFactorSourceIds<{ ROLE_CONFIRMATION }>; -pub type ConfirmationRoleWithFactorSourceIds = ConfirmationRoleWithFactorSourceIDs; +pub type ConfirmationRoleWithFactorSourceIds = + ConfirmationRoleWithFactorSourceIDs; impl HasSampleValues for ConfirmationRoleWithFactorSourceIds { /// Config MFA 1.1 From 3c8d57487f12529455499ab62ea6d47c6b5a9a41 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 11:59:29 +0100 Subject: [PATCH 09/70] [no ci] WIP --- .../mfa/security_structures/builder.rs | 6 +++++ .../security_structures/error_conversion.rs | 16 ++++++----- .../matrices/builder/mod.rs | 2 ++ .../builder/violation_to_error_conversion.rs | 7 +++++ .../security_structures/roles/builder/mod.rs | 2 ++ .../builder/violation_to_error_conversion.rs | 27 +++++++++++++++++++ crates/sargon/tests/vectors/main.rs | 2 +- 7 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs index ecf08bd6c..400bb5bb8 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -203,6 +203,7 @@ impl SecurityShieldBuilder { builder.add_factor_source_to_recovery_override( factor_source_id.clone().into(), ) + // .map_err(|e| Into::::into((RoleKind::Recovery, e))) }) } @@ -214,6 +215,7 @@ impl SecurityShieldBuilder { builder.add_factor_source_to_confirmation_override( factor_source_id.clone().into(), ) + // .map_err(|e| Into::::into((RoleKind::Confirmation, e))) }) } @@ -225,6 +227,7 @@ impl SecurityShieldBuilder { builder.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( factor_source_kind.clone().into(), ) + .map_err(|e| Into::::into((RoleKind::Confirmation, e))) }) } @@ -236,6 +239,7 @@ impl SecurityShieldBuilder { builder.validation_for_addition_of_factor_source_of_kind_to_recovery_override( factor_source_kind.clone().into(), ) + .map_err(|e| Into::::into((RoleKind::Recovery, e))) }) } @@ -247,6 +251,7 @@ impl SecurityShieldBuilder { builder.validation_for_addition_of_factor_source_of_kind_to_primary_override( factor_source_kind.clone().into(), ) + .map_err(|e| Into::::into((RoleKind::Primary, e))) }) } @@ -258,6 +263,7 @@ impl SecurityShieldBuilder { builder.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( factor_source_kind.clone().into(), ) + .map_err(|e| Into::::into((RoleKind::Primary, e))) }) } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs index 1100dc906..d5a785822 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs @@ -3,15 +3,17 @@ use sargon::{MatrixBuilderValidation, RoleBuilderValidation}; use crate::prelude::*; impl From for CommonError { - fn from(_value: MatrixBuilderValidation) -> Self { - // CommonError::BuildError(format!("{:?}", val)) - CommonError::Unknown + fn from(value: MatrixBuilderValidation) -> Self { + let sargon_err = Into::::into(value); + Into::::into(sargon_err) } } -impl From for CommonError { - fn from(_value: RoleBuilderValidation) -> Self { - // CommonError::BuildError(format!("{:?}", val)) - CommonError::Unknown +impl From<(RoleKind, sargon::RoleBuilderValidation)> for CommonError { + fn from(value: (RoleKind, RoleBuilderValidation)) -> Self { + let (role, violation) = value; + let role = Into::::into(role); + let sargon_err = Into::::into((role, violation)); + Into::::into(sargon_err) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs index 4e87cc622..208bac4a8 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs @@ -2,8 +2,10 @@ mod error; mod matrix_builder; mod matrix_builder_unit_tests; mod matrix_template; +mod violation_to_error_conversion; pub use error::*; #[allow(unused_imports)] pub use matrix_builder::*; pub use matrix_template::*; +pub use violation_to_error_conversion::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs new file mode 100644 index 000000000..a4a4aa665 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs @@ -0,0 +1,7 @@ +use crate::prelude::*; + +impl From for CommonError { + fn from(_value: MatrixBuilderValidation) -> Self { + todo!() + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs index af9dc07f3..40e7a0877 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs @@ -3,5 +3,7 @@ mod primary_roles_builder_unit_tests; mod recovery_roles_builder_unit_tests; mod roles_builder; mod roles_builder_unit_tests; +mod violation_to_error_conversion; pub use roles_builder::*; +pub use violation_to_error_conversion::*; \ No newline at end of file diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs new file mode 100644 index 000000000..c3ce0f510 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs @@ -0,0 +1,27 @@ +use crate::prelude::*; + +impl From for CommonError { + fn from(_value: BasicViolation) -> Self { + todo!() + } +} +impl From for CommonError { + fn from(_value: ForeverInvalidReason) -> Self { + todo!() + } +} +impl From for CommonError { + fn from(_value: NotYetValidReason) -> Self { + todo!() + } +} +impl From<(RoleKind, RoleBuilderValidation)> for CommonError { + fn from(value: (RoleKind, RoleBuilderValidation)) -> Self { + let (_role, violation) = value; + match violation { + RoleBuilderValidation::BasicViolation(val) => val.into(), + RoleBuilderValidation::ForeverInvalid(val) => val.into(), + RoleBuilderValidation::NotYetValid(val) => val.into(), + } + } +} diff --git a/crates/sargon/tests/vectors/main.rs b/crates/sargon/tests/vectors/main.rs index e2402dd5a..8c97fbd54 100644 --- a/crates/sargon/tests/vectors/main.rs +++ b/crates/sargon/tests/vectors/main.rs @@ -395,7 +395,7 @@ mod encrypted_profile_tests { .map(|x| x.security_state) .for_each(test); - Ok(()) + Ok::<(), CommonError>(()) })?; Ok(()) } From 22958f89c03c6eda1b73b8fc642ddc6535015ee2 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 12:01:32 +0100 Subject: [PATCH 10/70] TODO: Error conversion for roles and matrices --- .../roles/builder/violation_to_error_conversion.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs index c3ce0f510..6d430f0f4 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs @@ -2,17 +2,17 @@ use crate::prelude::*; impl From for CommonError { fn from(_value: BasicViolation) -> Self { - todo!() + CommonError::Unknown } } impl From for CommonError { fn from(_value: ForeverInvalidReason) -> Self { - todo!() + CommonError::Unknown } } impl From for CommonError { fn from(_value: NotYetValidReason) -> Self { - todo!() + CommonError::Unknown } } impl From<(RoleKind, RoleBuilderValidation)> for CommonError { From 07800a032a0baa9b1e9c73b4a8b485b90becc921 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 12:01:45 +0100 Subject: [PATCH 11/70] fix --- .../src/profile/mfa/security_structures/roles/builder/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs index 40e7a0877..cf23f6526 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs @@ -6,4 +6,4 @@ mod roles_builder_unit_tests; mod violation_to_error_conversion; pub use roles_builder::*; -pub use violation_to_error_conversion::*; \ No newline at end of file +pub use violation_to_error_conversion::*; From 6dfbc6613de83823ad32773fb0a0bb6e293df4a8 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 20:50:59 +0100 Subject: [PATCH 12/70] moar tests --- ...rityStructureMetadata+Wrap+Functions.swift | 1 + ...uctureOfFactorSources+Wrap+Functions.swift | 16 ------------- .../TransactionManifest+Wrap+Functions.swift | 4 +--- .../TestCases/Prelude/Decimal192Tests.swift | 2 +- ...ecurityStructureOfFactorSourcesTests.swift | 10 -------- .../RET/TransactionManifestTests.swift | 2 +- .../matrices/decl_matrix_macro.rs | 24 ++++++++++++------- .../matrices/matrix_of_factor_sources.rs | 19 +++++++++++++++ .../roles/decl_role_macro.rs | 20 ++++++++++------ .../factor_instance/badge_virtual_source.rs | 5 ++++ .../factor_instance/factor_instance_badge.rs | 2 ++ .../src/profile/v100/factors/factor_source.rs | 2 ++ 12 files changed, 60 insertions(+), 47 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift index 978b895f0..ab74ab258 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift @@ -6,3 +6,4 @@ extension SecurityStructureMetadata { self = newSecurityStructureMetadataNamed(name: name) } } + diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift index f628ec8ef..18ddf789a 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift @@ -1,19 +1,3 @@ import Foundation import SargonUniFFI -extension SecurityStructureOfFactorSources { - public init( - metadata: SecurityStructureMetadata, - numberOfDaysUntilAutoConfirmation: UInt16, - matrixOfFactors: MatrixOfFactorSources - ) { - assert(matrixOfFactors.primaryRole.thresholdFactors.count >= matrixOfFactors.primaryRole.threshold) - assert(matrixOfFactors.recoveryRole.thresholdFactors.count >= matrixOfFactors.recoveryRole.threshold) - assert(matrixOfFactors.confirmationRole.thresholdFactors.count >= matrixOfFactors.confirmationRole.threshold) - self = newSecurityStructureOfFactorSourcesAutoInDays( - metadata: metadata, - numberOfDaysUntilAutoConfirmation: numberOfDaysUntilAutoConfirmation, - matrixOfFactors: matrixOfFactors - ) - } -} diff --git a/apple/Sources/Sargon/Extensions/Methods/RET/TransactionManifest+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/RET/TransactionManifest+Wrap+Functions.swift index b77bc7bff..c905be2df 100644 --- a/apple/Sources/Sargon/Extensions/Methods/RET/TransactionManifest+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/RET/TransactionManifest+Wrap+Functions.swift @@ -36,8 +36,6 @@ extension TransactionManifest { } public var summary: ManifestSummary { - get throws { - try transactionManifestSummary(manifest: self) - } + transactionManifestSummary(manifest: self) } } diff --git a/apple/Tests/TestCases/Prelude/Decimal192Tests.swift b/apple/Tests/TestCases/Prelude/Decimal192Tests.swift index c06117c50..be5bd90a6 100644 --- a/apple/Tests/TestCases/Prelude/Decimal192Tests.swift +++ b/apple/Tests/TestCases/Prelude/Decimal192Tests.swift @@ -481,7 +481,7 @@ final class Decimal192Tests: Test { try largeDecimalsStrings.forEach(testLarge) XCTAssertLessThan(SUT.min.asDouble, SUT.max.asDouble) - XCTAssertNoThrow(try SUT("12345678987654321.000000000000000001").asDouble) + XCTAssertNoThrow(SUT("12345678987654321.000000000000000001").asDouble) } private var smallDecimalStrings: [String] { diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift index 00ce01cdf..017a9765d 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift @@ -5,16 +5,6 @@ import SargonUniFFI import XCTest final class SecurityStructureOfFactorSourcesTests: Test { - func test_new_from_auto_in_days() { - let sut = SUT( - metadata: .sample, - numberOfDaysUntilAutoConfirmation: 10, - matrixOfFactors: .sample - ) - XCTAssertEqual(sut.numberOfEpochsUntilAutoConfirmation, 2880) - XCTAssertEqual(sut.metadata, .sample) - XCTAssertEqual(sut.matrixOfFactors, .sample) - } func test_id() { eachSample { sut in diff --git a/apple/Tests/TestCases/RET/TransactionManifestTests.swift b/apple/Tests/TestCases/RET/TransactionManifestTests.swift index a074e5df3..12b0a642f 100644 --- a/apple/Tests/TestCases/RET/TransactionManifestTests.swift +++ b/apple/Tests/TestCases/RET/TransactionManifestTests.swift @@ -36,7 +36,7 @@ final class TransactionManifestTests: Test { } func test_manifest_summary() throws { - XCTAssertNoDifference(try SUT.sample.summary.addressesOfAccountsWithdrawnFrom, [AccountAddress.sampleMainnet]) + XCTAssertNoDifference(SUT.sample.summary.addressesOfAccountsWithdrawnFrom, [AccountAddress.sampleMainnet]) } func test_from_instructions_string_with_max_sbor_depth_is_ok() throws { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs index 2ff7920c7..65ca4e634 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs @@ -18,16 +18,21 @@ macro_rules! matrix_conversion { // Impl From -> Internal (to_internal: $internal_factor:ty => $uniffi:ident, $internal:ident) => { + impl $uniffi { + pub fn into_internal(&self) -> $internal { + unsafe { + <$internal>::unbuilt_with_roles_and_days( + self.primary_role.clone().into(), + self.recovery_role.clone().into(), + self.confirmation_role.clone().into(), + self.number_of_days_until_auto_confirm, + ) + } + } + } impl From<$uniffi> for $internal { fn from(value: $uniffi) -> Self { - unsafe { - Self::unbuilt_with_roles_and_days( - value.primary_role.into(), - value.recovery_role.into(), - value.confirmation_role.into(), - value.number_of_days_until_auto_confirm, - ) - } + value.into_internal() } } }; @@ -46,7 +51,7 @@ macro_rules! matrix_conversion { $internal_name::sample().into() } fn sample_other() -> Self { - $internal_name::sample().into() + $internal_name::sample_other().into() } } @@ -74,6 +79,7 @@ macro_rules! matrix_conversion { } use sargon::$struct_name as [< Internal $struct_name>]; + delegate_debug_into!($struct_name, [< Internal $struct_name>]); matrix_conversion!( impl_from: [< Internal $factor_level>] => $struct_name => [< Internal $struct_name >] ); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs index 44749f199..9a2afc118 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs @@ -3,3 +3,22 @@ use crate::prelude::*; use super::decl_matrix_macro::matrix_conversion; matrix_conversion!(FactorSource); + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = MatrixOfFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs index 589c2fc7b..43cc63078 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs @@ -61,17 +61,22 @@ macro_rules! role_conversion_inner { // Impl From -> Internal (to_internal: $internal_factor:ty => $uniffi:ident, $internal:ident) => { - impl From<$uniffi> for $internal { - fn from(value: $uniffi) -> Self { + impl $uniffi { + pub fn into_internal(&self) -> $internal { unsafe { - Self::unbuilt_with_factors( - value.threshold, - value.threshold_factors.into_iter().map(|x| Into::<$internal_factor>::into(x.clone())).collect::>(), - value.override_factors.into_iter().map(|x| Into::<$internal_factor>::into(x.clone())).collect::>(), + <$internal>::unbuilt_with_factors( + self.threshold, + self.threshold_factors.clone().into_iter().map(|x| Into::<$internal_factor>::into(x.clone())).collect::>(), + self.override_factors.clone().into_iter().map(|x| Into::<$internal_factor>::into(x.clone())).collect::>(), ) } } } + impl From<$uniffi> for $internal { + fn from(value: $uniffi) -> Self { + value.into_internal() + } + } }; // Impl `From for X` and `From for InternalX` @@ -89,7 +94,7 @@ macro_rules! role_conversion_inner { $internal_name::sample().into() } fn sample_other() -> Self { - $internal_name::sample().into() + $internal_name::sample_other().into() } } @@ -129,6 +134,7 @@ macro_rules! role_conversion_inner { } paste! { use sargon::$struct_name as [< Internal $struct_name>]; + delegate_debug_into!($struct_name, [< Internal $struct_name>]); role_conversion_inner!( impl_from: [< Internal $factor_level>] => $struct_name => [< Internal $struct_name >] ); diff --git a/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/badge_virtual_source.rs b/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/badge_virtual_source.rs index af86e8458..a58e7e9d0 100644 --- a/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/badge_virtual_source.rs +++ b/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/badge_virtual_source.rs @@ -7,3 +7,8 @@ pub enum FactorInstanceBadgeVirtualSource { value: HierarchicalDeterministicPublicKey, }, } + +delegate_debug_into!( + FactorInstanceBadgeVirtualSource, + InternalFactorInstanceBadgeVirtualSource +); diff --git a/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs b/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs index 2ed30c56b..85caed183 100644 --- a/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs +++ b/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs @@ -13,3 +13,5 @@ pub enum FactorInstanceBadge { value: ResourceAddress, }, } + +delegate_debug_into!(FactorInstanceBadge, InternalFactorInstanceBadge); \ No newline at end of file diff --git a/crates/sargon-uniffi/src/profile/v100/factors/factor_source.rs b/crates/sargon-uniffi/src/profile/v100/factors/factor_source.rs index 0256444e1..2f7e3a222 100644 --- a/crates/sargon-uniffi/src/profile/v100/factors/factor_source.rs +++ b/crates/sargon-uniffi/src/profile/v100/factors/factor_source.rs @@ -36,6 +36,8 @@ pub enum FactorSource { }, } +delegate_debug_into!(FactorSource, InternalFactorSource); + #[uniffi::export] pub fn factor_source_to_string(factor_source: &FactorSource) -> String { factor_source.into_internal().to_string() From e3ddb6a0fd856d879501f4e4a0a8423f5617a846 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 20:51:23 +0100 Subject: [PATCH 13/70] more tests --- .../v100/factors/factor_instance/factor_instance_badge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs b/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs index 85caed183..9d2156ad6 100644 --- a/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs +++ b/crates/sargon-uniffi/src/profile/v100/factors/factor_instance/factor_instance_badge.rs @@ -14,4 +14,4 @@ pub enum FactorInstanceBadge { }, } -delegate_debug_into!(FactorInstanceBadge, InternalFactorInstanceBadge); \ No newline at end of file +delegate_debug_into!(FactorInstanceBadge, InternalFactorInstanceBadge); From 76fbb62b642b45bcbea0c9c46b015808e4273db6 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 20:59:58 +0100 Subject: [PATCH 14/70] bump typos and fix typos --- .pre-commit-config.yaml | 2 +- apple/Tests/TestCases/Prelude/Decimal192Tests.swift | 4 ++-- .../hierarchical_deterministic/cap26/paths/account_path.rs | 2 +- .../hierarchical_deterministic/cap26/paths/identity_path.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de9d3a614..d49c99393 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ default_install_hook_types: [pre-push] default_stages: [pre-push] repos: - repo: https://github.com/crate-ci/typos - rev: v1.22.7 + rev: v1.28.1 hooks: - id: typos - repo: local diff --git a/apple/Tests/TestCases/Prelude/Decimal192Tests.swift b/apple/Tests/TestCases/Prelude/Decimal192Tests.swift index be5bd90a6..0c5e1ee50 100644 --- a/apple/Tests/TestCases/Prelude/Decimal192Tests.swift +++ b/apple/Tests/TestCases/Prelude/Decimal192Tests.swift @@ -223,7 +223,7 @@ final class Decimal192Tests: Test { // Every number multiplied by zero, is zero... XCTAssertEqual(item * SUT.zero, SUT.zero) } - // ... incliding `max` and `min` + // ... including `max` and `min` XCTAssertEqual(SUT.max * 0, 0) XCTAssertEqual(SUT.min * 0, 0) @@ -239,7 +239,7 @@ final class Decimal192Tests: Test { // All numbers divided by themselves equals `one`... XCTAssertEqual(item / item, SUT.one) } - // ... incliding `max` and `min` + // ... including `max` and `min` XCTAssertEqual(SUT.max / SUT.max, SUT.one) XCTAssertEqual(SUT.min / SUT.min, SUT.one) } diff --git a/crates/sargon/src/hierarchical_deterministic/cap26/paths/account_path.rs b/crates/sargon/src/hierarchical_deterministic/cap26/paths/account_path.rs index 3cd446ae1..98b55b9b1 100644 --- a/crates/sargon/src/hierarchical_deterministic/cap26/paths/account_path.rs +++ b/crates/sargon/src/hierarchical_deterministic/cap26/paths/account_path.rs @@ -12,7 +12,7 @@ use crate::prelude::*; /// m / purpose' / coin_type' / network' / entity_kind' / key_kind' / entity_index' /// ``` /// -/// The `AccountPath` struct is parametrized by Radix network id and account index, but fixes the other +/// The `AccountPath` struct is parameterized by Radix network id and account index, but fixes the other /// constants in the path as follows: /// /// ```text diff --git a/crates/sargon/src/hierarchical_deterministic/cap26/paths/identity_path.rs b/crates/sargon/src/hierarchical_deterministic/cap26/paths/identity_path.rs index 671e80929..9cb485029 100644 --- a/crates/sargon/src/hierarchical_deterministic/cap26/paths/identity_path.rs +++ b/crates/sargon/src/hierarchical_deterministic/cap26/paths/identity_path.rs @@ -12,7 +12,7 @@ use crate::prelude::*; /// m / purpose' / coin_type' / network' / entity_kind' / key_kind' / entity_index' /// ``` /// -/// The `IdentityPath` struct is parametrized by Radix network id and entity index, but fixes the other +/// The `IdentityPath` struct is parameterized by Radix network id and entity index, but fixes the other /// constants in the path as follows: /// /// ```text From 895caf417ff6acc562e88db554a02d9d39081114 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 21:02:42 +0100 Subject: [PATCH 15/70] swiftformat --- .../FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift | 2 +- .../Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift | 2 +- .../Drivers/ProfileStateChange/ProfileStateChange.swift | 2 +- .../UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift | 2 +- .../MFA/SecurityStructureMetadata+Wrap+Functions.swift | 1 - .../SecurityStructureOfFactorSources+Wrap+Functions.swift | 1 - .../Derivation/BIP39/BIP39Language+Swiftified.swift | 2 +- .../Derivation/BIP39/BIP39WordCount+Swiftified.swift | 6 +++--- .../Crypto/Derivation/BIP39Word+Swiftified.swift | 4 ++-- .../Crypto/Derivation/BIP44LikePath+Swiftified.swift | 4 ++-- .../Swiftified/Crypto/SLIP10Curve+Swiftified.swift | 8 ++++---- .../Swiftified/Prelude/NetworkID+Swiftified.swift | 6 +++--- .../Prelude/NonFungibleGlobalID+Swiftified.swift | 2 +- .../Prelude/NonFungibleLocalID+Swiftified.swift | 4 ++-- .../Prelude/NonFungibleLocalIDString+Swiftified.swift | 2 +- .../Swiftified/Prelude/SargonError+Swiftified.swift | 8 ++++---- .../Profile/Account/AppearanceID+Swiftified.swift | 8 ++++---- .../Factor/FactorSource/FactorSourceID+Swiftified.swift | 6 +++--- .../FactorSourceIDFromAddress+Swiftified.swift | 6 +++--- .../FactorSource/FactorSourceIDFromHash+Swiftified.swift | 6 +++--- .../RadixConnect/P2PLinks/P2PLink+Swiftified.swift | 6 +++--- .../Swiftified/System/BIOS/BIOS+Swiftified.swift | 2 +- .../Swiftified/System/Drivers/Drivers+Swiftified.swift | 2 +- .../Swiftified/System/SargonOS+Swiftified.swift | 2 +- .../MFA/SecurityStructureOfFactorSourcesTests.swift | 1 - .../Backend/Sources/Planbok/Dependencies/Keychain.swift | 2 +- .../Planbok/Features/OverlayWindow/HUDFeature.swift | 2 +- .../Backend/Sources/Planbok/SharedState/SargonKey.swift | 4 ++-- 28 files changed, 50 insertions(+), 53 deletions(-) diff --git a/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift b/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift index 8d380bb38..09cc9cd34 100644 --- a/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift +++ b/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift @@ -1,7 +1,7 @@ import Foundation import SargonUniFFI -// MARK: - FileManager + @unchecked Sendable +// MARK: - FileManager + Sendable extension FileManager: @unchecked Sendable {} // Makes it possible to type `.shared` on an initalizer/func taking diff --git a/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift b/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift index f5ee3b423..31a007fb1 100644 --- a/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift +++ b/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift @@ -100,7 +100,7 @@ extension LogLevel: CaseIterable { public static let allCases: [Self] = rustLoggerGetAllLevels() } -// MARK: - Logger + @unchecked Sendable +// MARK: - Logger + Sendable extension Logger: @unchecked Sendable {} extension OSLogType { diff --git a/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift b/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift index 5dbc3b612..5c6611dd8 100644 --- a/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift +++ b/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift @@ -2,7 +2,7 @@ import SargonUniFFI public typealias ProfileStateChangeEventPublisher = EventPublisher -// MARK: - ProfileStateChangeEventPublisher + ProfileStateChangeDriver +// MARK: ProfileStateChangeDriver extension ProfileStateChangeEventPublisher: ProfileStateChangeDriver { public static let shared = ProfileStateChangeEventPublisher() diff --git a/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift b/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift index 00f29187a..2b22bc171 100644 --- a/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift +++ b/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift @@ -1,7 +1,7 @@ import Foundation import SargonUniFFI -// MARK: - UserDefaults + @unchecked Sendable +// MARK: - UserDefaults + Sendable extension UserDefaults: @unchecked Sendable {} // Makes it possible to type `.shared` on an initalizer/func taking diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift index ab74ab258..978b895f0 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureMetadata+Wrap+Functions.swift @@ -6,4 +6,3 @@ extension SecurityStructureMetadata { self = newSecurityStructureMetadataNamed(name: name) } } - diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift index 18ddf789a..61da91bb3 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/MFA/SecurityStructureOfFactorSources+Wrap+Functions.swift @@ -1,3 +1,2 @@ import Foundation import SargonUniFFI - diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift index f4b5a5716..0b82fee5e 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift @@ -3,5 +3,5 @@ import SargonUniFFI public typealias BIP39Language = Bip39Language -// MARK: - BIP39Language + SargonModel +// MARK: SargonModel extension BIP39Language: SargonModel {} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift index 1cc6527e1..e08d21239 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias BIP39WordCount = Bip39WordCount -// MARK: - BIP39WordCount + SargonModel +// MARK: SargonModel extension BIP39WordCount: SargonModel { public static let sample: Self = .twentyFour public static let sampleOther: Self = .twelve @@ -15,7 +15,7 @@ extension BIP39WordCount { } } -// MARK: - BIP39WordCount + Identifiable +// MARK: Identifiable extension BIP39WordCount: Identifiable { public typealias ID = RawValue public var id: ID { @@ -23,7 +23,7 @@ extension BIP39WordCount: Identifiable { } } -// MARK: - BIP39WordCount + Comparable +// MARK: Comparable extension BIP39WordCount: Comparable { public static func < (lhs: Self, rhs: Self) -> Bool { lhs.rawValue < rhs.rawValue diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift index dd0490d4a..15010a9f0 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift @@ -2,10 +2,10 @@ import SargonUniFFI public typealias BIP39Word = Bip39Word -// MARK: - BIP39Word + SargonModel +// MARK: SargonModel extension BIP39Word: SargonModel {} -// MARK: - BIP39Word + Identifiable +// MARK: Identifiable extension BIP39Word: Identifiable { public typealias ID = U11 diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift index 8f4e854c9..cba7536c6 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias BIP44LikePath = Bip44LikePath -// MARK: - BIP44LikePath + SargonModel, DerivationPathProtocol +// MARK: SargonModel, DerivationPathProtocol extension BIP44LikePath: SargonModel, DerivationPathProtocol { public var asGeneral: DerivationPath { .bip44Like(value: self) @@ -14,7 +14,7 @@ extension BIP44LikePath: SargonModel, DerivationPathProtocol { } } -// MARK: - BIP44LikePath + CustomStringConvertible +// MARK: CustomStringConvertible extension BIP44LikePath: CustomStringConvertible { public var description: String { toString() diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift index 44748ee04..04f03d82d 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift @@ -2,10 +2,10 @@ import SargonUniFFI public typealias SLIP10Curve = Slip10Curve -// MARK: - SLIP10Curve + SargonModel +// MARK: SargonModel extension SLIP10Curve: SargonModel {} -// MARK: - SLIP10Curve + Identifiable +// MARK: Identifiable extension SLIP10Curve: Identifiable { public typealias ID = String public var id: ID { @@ -13,7 +13,7 @@ extension SLIP10Curve: Identifiable { } } -// MARK: - SLIP10Curve + CustomStringConvertible +// MARK: CustomStringConvertible extension SLIP10Curve: CustomStringConvertible { public var description: String { toString() @@ -26,5 +26,5 @@ extension SLIP10Curve { } } -// MARK: - SLIP10Curve + SargonStringCodable +// MARK: SargonStringCodable extension SLIP10Curve: SargonStringCodable {} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift index 0f6291801..ee98052d7 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift @@ -3,17 +3,17 @@ import SargonUniFFI public typealias NetworkID = NetworkId -// MARK: - NetworkID + SargonModel +// MARK: SargonModel extension NetworkID: SargonModel {} -// MARK: - NetworkID + CustomStringConvertible +// MARK: CustomStringConvertible extension NetworkID: CustomStringConvertible { public var description: String { toString() } } -// MARK: - NetworkID + Codable +// MARK: Codable extension NetworkID: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift index fb38bc221..a0bad729e 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift @@ -2,7 +2,7 @@ import SargonUniFFI public typealias NonFungibleGlobalID = NonFungibleGlobalId -// MARK: - NonFungibleGlobalID + IdentifiableByStringProtocol +// MARK: IdentifiableByStringProtocol extension NonFungibleGlobalID: IdentifiableByStringProtocol { public var localID: NonFungibleLocalID { nonFungibleLocalId diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift index 882ad6428..ba577a6ea 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift @@ -3,10 +3,10 @@ import SargonUniFFI public typealias NonFungibleLocalID = NonFungibleLocalId -// MARK: - NonFungibleLocalID + IdentifiableByStringProtocol +// MARK: IdentifiableByStringProtocol extension NonFungibleLocalID: IdentifiableByStringProtocol {} -// MARK: - NonFungibleLocalID + ExpressibleByIntegerLiteral +// MARK: ExpressibleByIntegerLiteral extension NonFungibleLocalID: ExpressibleByIntegerLiteral { public init(integerLiteral value: UInt64) { self.init(integer: value) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift index 73e1e6489..193307ec8 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias NonFungibleLocalIDString = NonFungibleLocalIdString -// MARK: - NonFungibleLocalIDString + SargonModel +// MARK: SargonModel extension NonFungibleLocalIDString: SargonModel {} #if DEBUG diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift index 8ad3267ff..2cc184abf 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift @@ -3,24 +3,24 @@ import SargonUniFFI public typealias SargonError = CommonError -// MARK: - SargonError + SargonModel +// MARK: SargonModel extension SargonError: SargonModel {} -// MARK: - SargonError + CustomDebugStringConvertible +// MARK: CustomDebugStringConvertible extension SargonError: CustomDebugStringConvertible { public var debugDescription: String { "\(errorCode): \(errorMessage)" } } -// MARK: - SargonError + CustomStringConvertible +// MARK: CustomStringConvertible extension SargonError: CustomStringConvertible { public var description: String { errorMessage } } -// MARK: - SargonError + LocalizedError +// MARK: LocalizedError extension SargonError: LocalizedError { public var errorDescription: String? { let errorCodeFormatted = "Error code: \(errorCode)" diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift index acc2441b5..39a8ab7be 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift @@ -2,10 +2,10 @@ import SargonUniFFI public typealias AppearanceID = AppearanceId -// MARK: - AppearanceID + SargonModel +// MARK: SargonModel extension AppearanceID: SargonModel {} -// MARK: - AppearanceID + Identifiable +// MARK: Identifiable extension AppearanceID: Identifiable { public typealias ID = UInt8 public var id: ID { @@ -13,14 +13,14 @@ extension AppearanceID: Identifiable { } } -// MARK: - AppearanceID + CustomStringConvertible +// MARK: CustomStringConvertible extension AppearanceID: CustomStringConvertible { public var description: String { value.description } } -// MARK: - AppearanceID + Codable +// MARK: Codable extension AppearanceID: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift index 481b3f09d..d60b959ae 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift @@ -3,17 +3,17 @@ import SargonUniFFI public typealias FactorSourceID = FactorSourceId -// MARK: - FactorSourceID + SargonModel +// MARK: SargonModel extension FactorSourceID: SargonModel {} -// MARK: - FactorSourceID + CustomStringConvertible +// MARK: CustomStringConvertible extension FactorSourceID: CustomStringConvertible { public var description: String { toString() } } -// MARK: - FactorSourceID + FactorSourceIDProtocol +// MARK: FactorSourceIDProtocol extension FactorSourceID: FactorSourceIDProtocol { public var asGeneral: FactorSourceID { self diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift index 70232feb7..9a15a05b6 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift @@ -3,13 +3,13 @@ import SargonUniFFI public typealias FactorSourceIDFromAddress = FactorSourceIdFromAddress -// MARK: - FactorSourceIDFromAddress + SargonModel +// MARK: SargonModel extension FactorSourceIDFromAddress: SargonModel {} -// MARK: - FactorSourceIDFromAddress + SargonObjectCodable +// MARK: SargonObjectCodable extension FactorSourceIDFromAddress: SargonObjectCodable {} -// MARK: - FactorSourceIDFromAddress + FactorSourceIDSpecificProtocol +// MARK: FactorSourceIDSpecificProtocol extension FactorSourceIDFromAddress: FactorSourceIDSpecificProtocol { public var asGeneral: FactorSourceID { .address(value: self) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift index 0070f7ff3..59a69fb07 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift @@ -3,13 +3,13 @@ import SargonUniFFI public typealias FactorSourceIDFromHash = FactorSourceIdFromHash -// MARK: - FactorSourceIDFromHash + SargonModel +// MARK: SargonModel extension FactorSourceIDFromHash: SargonModel {} -// MARK: - FactorSourceIDFromHash + SargonObjectCodable +// MARK: SargonObjectCodable extension FactorSourceIDFromHash: SargonObjectCodable {} -// MARK: - FactorSourceIDFromHash + FactorSourceIDSpecificProtocol +// MARK: FactorSourceIDSpecificProtocol extension FactorSourceIDFromHash: FactorSourceIDSpecificProtocol { public var asGeneral: FactorSourceID { .hash(value: self) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift index 004761de5..876209393 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift @@ -3,13 +3,13 @@ import SargonUniFFI public typealias P2PLink = P2pLink -// MARK: - P2PLink + SargonModel +// MARK: SargonModel extension P2PLink: SargonModel {} -// MARK: - P2PLink + SargonObjectCodable +// MARK: SargonObjectCodable extension P2PLink: SargonObjectCodable {} -// MARK: - P2PLink + Identifiable +// MARK: Identifiable extension P2PLink: Identifiable { public typealias ID = PublicKeyHash } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift index 82ba53526..49a19e1fe 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias BIOS = Bios -// MARK: - BIOS + @unchecked Sendable +// MARK: Sendable extension BIOS: @unchecked Sendable {} extension BIOS { diff --git a/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift index 5bfe9c472..6e033bdd2 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift @@ -1,7 +1,7 @@ import Foundation import SargonUniFFI -// MARK: - Drivers + @unchecked Sendable +// MARK: - Drivers + Sendable extension Drivers: @unchecked Sendable {} extension Drivers { diff --git a/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift index 703d15e7d..a902b7cde 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift @@ -3,5 +3,5 @@ import SargonUniFFI public typealias SargonOS = SargonOs -// MARK: - SargonOS + @unchecked Sendable +// MARK: Sendable extension SargonOS: @unchecked Sendable {} diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift index 017a9765d..c89dee7ec 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityStructureOfFactorSourcesTests.swift @@ -5,7 +5,6 @@ import SargonUniFFI import XCTest final class SecurityStructureOfFactorSourcesTests: Test { - func test_id() { eachSample { sut in XCTAssertEqual(sut.id, sut.metadata.id) diff --git a/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift b/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift index a05e36ffc..f24a9e7bb 100644 --- a/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift +++ b/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift @@ -2,7 +2,7 @@ import ComposableArchitecture @_exported import KeychainAccess import Sargon -// MARK: - Keychain + @unchecked Sendable +// MARK: - Keychain + Sendable extension Keychain: @unchecked Sendable {} // MARK: - Keychain + SecureStorageDriver diff --git a/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift b/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift index 740232c95..14937ceb8 100644 --- a/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift +++ b/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift @@ -133,7 +133,7 @@ extension View { } } -// MARK: - SwiftUI.Animation + @unchecked Sendable +// MARK: - SwiftUI.Animation + Sendable extension SwiftUI.Animation: @unchecked Sendable {} extension SwiftUI.Animation { diff --git a/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift b/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift index a1af8cb8e..4e9532c2e 100644 --- a/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift +++ b/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift @@ -2,10 +2,10 @@ import ComposableArchitecture import Foundation import Sargon -// MARK: - PersistenceKeyDefault + @unchecked Sendable +// MARK: - PersistenceKeyDefault + Sendable extension PersistenceKeyDefault: @unchecked Sendable where Base: Sendable {} -// MARK: - KeyPath + @unchecked Sendable +// MARK: - KeyPath + Sendable extension KeyPath: @unchecked Sendable where Root: Sendable, Value: Sendable {} // MARK: - SargonKey From 7507b7d02a38a53fe06bc3164c00f97760816b1e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 1 Dec 2024 22:48:07 +0100 Subject: [PATCH 16/70] error converesion --- .../src/core/error/common_error.rs | 71 +++++++++++++++++++ crates/sargon/src/core/error/common_error.rs | 71 +++++++++++++++++++ .../builder/violation_to_error_conversion.rs | 59 ++++++++++++++- .../builder/violation_to_error_conversion.rs | 62 ++++++++++++++-- 4 files changed, 255 insertions(+), 8 deletions(-) diff --git a/crates/sargon-uniffi/src/core/error/common_error.rs b/crates/sargon-uniffi/src/core/error/common_error.rs index f79fa2c64..20c213c7f 100644 --- a/crates/sargon-uniffi/src/core/error/common_error.rs +++ b/crates/sargon-uniffi/src/core/error/common_error.rs @@ -818,6 +818,77 @@ pub enum CommonError { #[error("Failed to encode transaction preview v2 - '{underlying}'")] FailedToEncodeTransactionPreviewV2 { underlying: String } = 10229, + + /// e.g. tried to remove a factor source which was not found. + #[error("FactorSourceID not found")] + FactorSourceNotFound = 10230, + + #[error("Recovery cannot set threshold")] + RecoveryCannotSetThreshold = 10231, + + #[error("Confirmation cannot set threshold")] + ConfirmationCannotSetThreshold = 10232, + + #[error("Factor source already present")] + FactorSourceAlreadyPresent = 10233, + + #[error("Primary role cannot have multiple devices")] + PrimaryCannotHaveMultipleDevices = 10234, + + #[error("Primary role cannot have password in override list")] + PrimaryCannotHavePasswordInOverrideList = 10235, + + #[error("Primary role cannot contain Security Questions")] + PrimaryCannotContainSecurityQuestions = 10236, + + #[error("Primary role cannot contain Trusted Contact")] + PrimaryCannotContainTrustedContact = 10237, + + #[error("Recovery role threshold list not supported")] + RecoveryRoleThresholdFactorsNotSupported = 10238, + + #[error("Recovery role Security Questions not supported")] + RecoveryRoleSecurityQuestionsNotSupported = 10239, + + #[error("Recovery role password not supported")] + RecoveryRolePasswordNotSupported = 10240, + + #[error("Confirmation role threshold list not supported")] + ConfirmationRoleThresholdFactorsNotSupported = 10241, + + #[error("Confirmation role cannot contain Trusted Contact")] + ConfirmationRoleTrustedContactNotSupported = 10242, + + #[error("Role must have at least one factor")] + RoleMustHaveAtLeastOneFactor = 10243, + + #[error( + "Primary role with password in threshold list must have another factor" + )] + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor = 10244, + + #[error( + "Primary role with threshold factors cannot have a threshold of zero" + )] + PrimaryRoleWithThresholdCannotBeZeroWithFactors = 10245, + + #[error("Primary role with password in threshold list must have threshold greater than one")] + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne = 10246, + + #[error("Threshold higher than threshold factors len")] + ThresholdHigherThanThresholdFactorsLen = 10247, + + #[error("The factor source was not found in any role")] + FactorSourceNotFoundInAnyRole = 10248, + + #[error("The number of days until auto confirm must be greater than zero")] + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero = 10249, + + #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] + RecoveryAndConfirmationFactorsOverlap = 10250, + + #[error("The single factor used in the primary role must not be used in any other role")] + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole = 10251, } #[uniffi::export] diff --git a/crates/sargon/src/core/error/common_error.rs b/crates/sargon/src/core/error/common_error.rs index b7bc32764..9b792f911 100644 --- a/crates/sargon/src/core/error/common_error.rs +++ b/crates/sargon/src/core/error/common_error.rs @@ -815,6 +815,77 @@ pub enum CommonError { #[error("Failed to encode transaction preview v2 - '{underlying}'")] FailedToEncodeTransactionPreviewV2 { underlying: String } = 10229, + + /// e.g. tried to remove a factor source which was not found. + #[error("FactorSourceID not found")] + FactorSourceNotFound = 10230, + + #[error("Recovery cannot set threshold")] + RecoveryCannotSetThreshold = 10231, + + #[error("Confirmation cannot set threshold")] + ConfirmationCannotSetThreshold = 10232, + + #[error("Factor source already present")] + FactorSourceAlreadyPresent = 10233, + + #[error("Primary role cannot have multiple devices")] + PrimaryCannotHaveMultipleDevices = 10234, + + #[error("Primary role cannot have password in override list")] + PrimaryCannotHavePasswordInOverrideList = 10235, + + #[error("Primary role cannot contain Security Questions")] + PrimaryCannotContainSecurityQuestions = 10236, + + #[error("Primary role cannot contain Trusted Contact")] + PrimaryCannotContainTrustedContact = 10237, + + #[error("Recovery role threshold list not supported")] + RecoveryRoleThresholdFactorsNotSupported = 10238, + + #[error("Recovery role Security Questions not supported")] + RecoveryRoleSecurityQuestionsNotSupported = 10239, + + #[error("Recovery role password not supported")] + RecoveryRolePasswordNotSupported = 10240, + + #[error("Confirmation role threshold list not supported")] + ConfirmationRoleThresholdFactorsNotSupported = 10241, + + #[error("Confirmation role cannot contain Trusted Contact")] + ConfirmationRoleTrustedContactNotSupported = 10242, + + #[error("Role must have at least one factor")] + RoleMustHaveAtLeastOneFactor = 10243, + + #[error( + "Primary role with password in threshold list must have another factor" + )] + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor = 10244, + + #[error( + "Primary role with threshold factors cannot have a threshold of zero" + )] + PrimaryRoleWithThresholdCannotBeZeroWithFactors = 10245, + + #[error("Primary role with password in threshold list must have threshold greater than one")] + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne = 10246, + + #[error("Threshold higher than threshold factors len")] + ThresholdHigherThanThresholdFactorsLen = 10247, + + #[error("The factor source was not found in any role")] + FactorSourceNotFoundInAnyRole = 10248, + + #[error("The number of days until auto confirm must be greater than zero")] + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero = 10249, + + #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] + RecoveryAndConfirmationFactorsOverlap = 10250, + + #[error("The single factor used in the primary role must not be used in any other role")] + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole = 10251, } impl CommonError { diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs index a4a4aa665..91df3d7ce 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs @@ -1,7 +1,62 @@ use crate::prelude::*; +impl From for CommonError { + fn from(value: MatrixRolesInCombinationBasicViolation) -> Self { + use MatrixRolesInCombinationBasicViolation::*; + match value { + FactorSourceNotFoundInAnyRole => { + CommonError::FactorSourceNotFoundInAnyRole + } + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero => { + CommonError::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero + } + } + } +} +impl From for CommonError { + fn from(value: MatrixRolesInCombinationForeverInvalid) -> Self { + use MatrixRolesInCombinationForeverInvalid::*; + match value { + RecoveryAndConfirmationFactorsOverlap => { + Self::RecoveryAndConfirmationFactorsOverlap + } + } + } +} +impl From for CommonError { + fn from(value: MatrixRolesInCombinationNotYetValid) -> Self { + use MatrixRolesInCombinationNotYetValid::*; + + match value { + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole => { + Self::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole + } + } + } +} + +impl From for CommonError { + fn from(value: MatrixRolesInCombinationViolation) -> Self { + match value { + MatrixRolesInCombinationViolation::Basic(basic) => basic.into(), + MatrixRolesInCombinationViolation::ForeverInvalid( + forever_invalid, + ) => forever_invalid.into(), + MatrixRolesInCombinationViolation::NotYetValid(not_yet_valid) => { + not_yet_valid.into() + } + } + } +} impl From for CommonError { - fn from(_value: MatrixBuilderValidation) -> Self { - todo!() + fn from(value: MatrixBuilderValidation) -> Self { + match value { + MatrixBuilderValidation::RoleInIsolation { role, violation } => { + (role, violation).into() + } + MatrixBuilderValidation::CombinationViolation(violation) => { + violation.into() + } + } } } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs index 6d430f0f4..25ba730a5 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs @@ -1,18 +1,68 @@ use crate::prelude::*; impl From for CommonError { - fn from(_value: BasicViolation) -> Self { - CommonError::Unknown + fn from(value: BasicViolation) -> Self { + match value { + BasicViolation::FactorSourceNotFound => { + CommonError::FactorSourceNotFound + } + BasicViolation::RecoveryCannotSetThreshold => { + CommonError::RecoveryCannotSetThreshold + } + BasicViolation::ConfirmationCannotSetThreshold => { + CommonError::ConfirmationCannotSetThreshold + } + } } } impl From for CommonError { - fn from(_value: ForeverInvalidReason) -> Self { - CommonError::Unknown + fn from(value: ForeverInvalidReason) -> Self { + use ForeverInvalidReason::*; + + match value { + FactorSourceAlreadyPresent => Self::FactorSourceAlreadyPresent, + PrimaryCannotHaveMultipleDevices => { + Self::PrimaryCannotHaveMultipleDevices + } + PrimaryCannotHavePasswordInOverrideList => { + Self::PrimaryCannotHavePasswordInOverrideList + } + PrimaryCannotContainSecurityQuestions => { + Self::PrimaryCannotContainSecurityQuestions + } + PrimaryCannotContainTrustedContact => { + Self::PrimaryCannotContainTrustedContact + } + RecoveryRoleThresholdFactorsNotSupported => { + Self::RecoveryRoleThresholdFactorsNotSupported + } + RecoveryRoleSecurityQuestionsNotSupported => { + Self::RecoveryRoleSecurityQuestionsNotSupported + } + RecoveryRolePasswordNotSupported => { + Self::RecoveryRolePasswordNotSupported + } + ConfirmationRoleThresholdFactorsNotSupported => { + Self::ConfirmationRoleThresholdFactorsNotSupported + } + ConfirmationRoleTrustedContactNotSupported => { + Self::ConfirmationRoleTrustedContactNotSupported + } + } } } impl From for CommonError { - fn from(_value: NotYetValidReason) -> Self { - CommonError::Unknown + fn from(value: NotYetValidReason) -> Self { + use NotYetValidReason::*; + match value { + RoleMustHaveAtLeastOneFactor => { + Self::RoleMustHaveAtLeastOneFactor + }, + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => Self::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + PrimaryRoleWithThresholdCannotBeZeroWithFactors => Self::PrimaryRoleWithThresholdCannotBeZeroWithFactors, + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne => Self::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + ThresholdHigherThanThresholdFactorsLen => Self::ThresholdHigherThanThresholdFactorsLen + } } } impl From<(RoleKind, RoleBuilderValidation)> for CommonError { From 738cb52ee4213a7e019ca0f8030f9f66b0891613 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 09:32:39 +0100 Subject: [PATCH 17/70] [no ci] WIP --- .../SecurityShieldBuilder+Swiftiefied.swift | 58 +++++++++++++++++++ ...ource_in_role_builder_validation_status.rs | 22 +++++++ .../security_structures/models/role_kind.rs | 2 +- 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift new file mode 100644 index 000000000..1829877ca --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift @@ -0,0 +1,58 @@ +// +// SecurityShieldBuilder+Swiftiefied.swift +// Sargon +// +// Created by Alexander Cyon on 2024-12-02. +// + +import SargonUniFFI + +extension SecurityShieldBuilder { + public typealias Factor = FactorSourceID + + /// Confirmation Role + public var numberOfDaysUntilAutoConfirm: UInt16 { + get { getNumberOfDaysUntilAutoConfirm() } + set { + precondition(newValue > 0, "Number of days until auto confirm must be greater than zero.") + try! setNumberOfDaysUntilAutoConfirm(numberOfDays: UInt16(newValue)) + } + } + + public var threshold: UInt8 { + get { + getPrimaryThreshold() + } + set { + precondition(newValue <= primaryRoleThresholdFactors.count, "Threshold must not me greater than the number of threshold factors in the primary role.") + try! setThreshold(threshold: newValue) + } + } + + public var primaryRoleThresholdFactors: [Factor] { + getPrimaryThresholdFactors() + } + + public var primaryRoleOverrideFactors: [Factor] { + getPrimaryOverrideFactors() + } + + public var recoveryRoleFactors: [Factor] { + getRecoveryFactors() + } + + public var confirmationRoleFactors: [Factor] { + getConfirmationFactors() + } + + /// Name of the shield + public var name: String { + get { + getName() + } + set { + setName(name: newValue) + } + } + +} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 5bcee31b2..157af579d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -7,6 +7,28 @@ pub struct FactorSourceValidationStatus { pub validation: sargon::RoleBuilderMutateResult, } +#[uniffi::export] +impl FactorSourceValidationStatus { + pub fn validation_err(&self) -> Option { + if let Err(e) = self + .validation + .map_err(|e| Into::::into((self.role, e))) + { + return Some(e); + } else { + None + } + } + + pub fn role(&self) -> RoleKind { + self.role + } + + pub fn factor_source_id(&self) -> FactorSourceID { + self.factor_source_id.clone() + } +} + impl From for FactorSourceValidationStatus { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs index 6a056b5fd..8560dc72f 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use sargon::RoleKind as InternalRoleKind; #[derive( - Clone, Debug, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum, + Clone, Copy, Debug, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum, )] pub enum RoleKind { /// The primary role of some matrix of factors From a7cbdefa328c1180521ab547f5fe0c24cd96530f Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 09:50:05 +0100 Subject: [PATCH 18/70] [no ci] wip --- .../SecurityShieldBuilder+Swiftiefied.swift | 8 +----- .../MFA/SecurityShieldsBuilderTests.swift | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift index 1829877ca..566343201 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift @@ -20,13 +20,7 @@ extension SecurityShieldBuilder { } public var threshold: UInt8 { - get { - getPrimaryThreshold() - } - set { - precondition(newValue <= primaryRoleThresholdFactors.count, "Threshold must not me greater than the number of threshold factors in the primary role.") - try! setThreshold(threshold: newValue) - } + getPrimaryThreshold() } public var primaryRoleThresholdFactors: [Factor] { diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift new file mode 100644 index 000000000..d743b9e84 --- /dev/null +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -0,0 +1,27 @@ +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import Testing + +@Suite("ShieldBuilder") +struct ShieldTests { + + @Test("Get/Set name") + func name() { + let builder = SecurityShieldBuilder() + #expect(builder.name == "My Shield") + builder.name = "S.H.I.E.L.D" + #expect(builder.name == "S.H.I.E.L.D") + } + + + @Test("Get/Set Threshold") + func threshold() { + let builder = SecurityShieldBuilder() + #expect(builder.threshold == 0) + #expect(throws: CommonError.RoleMustHaveAtLeastOneFactor) { + try builder.setThreshold(threshold: 0) + } + } +} From 7a9f659c22631a8eeaf19708b40fd99878b9dc8e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 10:33:53 +0100 Subject: [PATCH 19/70] [no ci] wip --- .../SecurityShieldBuilder+Swiftiefied.swift | 13 ++++++ .../MFA/SecurityShieldsBuilderTests.swift | 43 ++++++++++++++++++- .../mfa/security_structures/builder.rs | 23 ++++++---- .../matrices/matrix_of_factor_source_ids.rs | 32 ++++++++++++++ 4 files changed, 101 insertions(+), 10 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift index 566343201..77b6521f0 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift @@ -7,6 +7,19 @@ import SargonUniFFI +extension FactorSourceValidationStatus { + public var factorSourceID: FactorSourceID { + factorSourceId() + } + + public var validationError: CommonError? { + self.validationErr() + } + + public var role: RoleKind { + role() + } +} extension SecurityShieldBuilder { public typealias Factor = FactorSourceID diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index d743b9e84..54b7ea48e 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -7,7 +7,7 @@ import Testing @Suite("ShieldBuilder") struct ShieldTests { - @Test("Get/Set name") + @Test("name") func name() { let builder = SecurityShieldBuilder() #expect(builder.name == "My Shield") @@ -16,7 +16,7 @@ struct ShieldTests { } - @Test("Get/Set Threshold") + @Test("threshold") func threshold() { let builder = SecurityShieldBuilder() #expect(builder.threshold == 0) @@ -24,4 +24,43 @@ struct ShieldTests { try builder.setThreshold(threshold: 0) } } + + @Test("days") + func days() { + let builder = SecurityShieldBuilder() + #expect(builder.numberOfDaysUntilAutoConfirm == 14) + #expect(throws: CommonError.NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) { + try builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 0) + } + } + + @Test("empty primary threshold") + func emptyThresholdFactors() { + let builder = SecurityShieldBuilder() + #expect(builder.primaryRoleThresholdFactors == []) + } + + @Test("empty primary override") + func emptyOverrideFactors() { + let builder = SecurityShieldBuilder() + #expect(builder.primaryRoleOverrideFactors == []) + } + + @Test("empty recovery") + func emptyRecoveryFactors() { + let builder = SecurityShieldBuilder() + #expect(builder.recoveryRoleFactors == []) + } + + @Test("empty confirmation") + func emptyConfirmationFactors() { + let builder = SecurityShieldBuilder() + #expect(builder.confirmationRoleFactors == []) + } + + @Test("primary override validation status trustedContact") + func primValidationStatusTrustedContact() throws { + let builder = SecurityShieldBuilder() + try #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).map(\.validationError) == [CommonError.PrimaryCannotContainTrustedContact]) + } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs index 400bb5bb8..0487162a3 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -61,7 +61,7 @@ impl SecurityShieldBuilder { &IndexSet, ) -> IndexSet, - ) -> Result>, CommonError> { + ) -> Vec> { let input = &factor_sources .clone() .into_iter() @@ -78,6 +78,7 @@ impl SecurityShieldBuilder { Ok::<_, CommonError>(xs) }) + .expect("Internal error - have you already called `build` on this builder? You should not continue using the builder after it has been built.") } } @@ -203,7 +204,6 @@ impl SecurityShieldBuilder { builder.add_factor_source_to_recovery_override( factor_source_id.clone().into(), ) - // .map_err(|e| Into::::into((RoleKind::Recovery, e))) }) } @@ -215,7 +215,6 @@ impl SecurityShieldBuilder { builder.add_factor_source_to_confirmation_override( factor_source_id.clone().into(), ) - // .map_err(|e| Into::::into((RoleKind::Confirmation, e))) }) } @@ -270,7 +269,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( &self, factor_sources: Vec, - ) -> Result>, CommonError> { + ) -> Vec> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -283,7 +282,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( &self, factor_sources: Vec, - ) -> Result>, CommonError> { + ) -> Vec> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -295,7 +294,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( &self, factor_sources: Vec, - ) -> Result>, CommonError> { + ) -> Vec> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -308,7 +307,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( &self, factor_sources: Vec, - ) -> Result>, CommonError> { + ) -> Vec> { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -319,6 +318,10 @@ impl SecurityShieldBuilder { ) } + pub fn validate(&self) -> Result<(), CommonError> { + self.with(|builder| builder.validate()) + } + pub fn build( self: Arc, ) -> Result { @@ -553,7 +556,11 @@ mod tests { sut.remove_factor(FactorSourceID::sample_ledger_other()) .unwrap(); - let shield = sut.build().unwrap(); + let v0 = sut.validate(); + let v1 = sut.validate(); // can call validate many times! + assert_eq!(v0, v1); + + let shield = sut.build().unwrap(); // can build only once! (but can build after `validate`) assert_eq!(shield.wrapped.metadata.display_name.value, "S.H.I.E.L.D."); assert_eq!( shield diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 1e58f03e6..3118a60ca 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -3,3 +3,35 @@ use crate::prelude::*; use super::decl_matrix_macro::matrix_conversion; matrix_conversion!(FactorSourceID); + +macro_rules! export_sample_config { + ($config_ident:literal) => { + paste! { + #[uniffi::export] + pub fn []() -> MatrixOfFactorSourceIDs { + ::[< sample_config_ $config_ident>]().into() + } + } + }; +} + +pub(crate) use export_sample_config; + +export_sample_config!(11); +export_sample_config!(12); +export_sample_config!(13); +export_sample_config!(14); +export_sample_config!(15); +export_sample_config!(21); +export_sample_config!(22); +export_sample_config!(23); +export_sample_config!(24); +export_sample_config!(30); +export_sample_config!(40); +export_sample_config!(51); +export_sample_config!(52); +export_sample_config!(60); +export_sample_config!(70); +export_sample_config!(80); +export_sample_config!(90); + From 4d4279466a8f4e522130187221bf10269391b0b7 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 12:09:09 +0100 Subject: [PATCH 20/70] [no ci] build does not consume --- .../mfa/security_structures/builder.rs | 127 ++++++++++++++---- .../matrices/builder/error.rs | 31 ++++- .../matrices/builder/matrix_builder.rs | 63 ++++++++- .../builder/matrix_builder_unit_tests.rs | 17 ++- .../primary_roles_builder_unit_tests.rs | 5 + .../roles/builder/roles_builder.rs | 34 ++++- .../src/signing/petition_types/role_kind.rs | 18 ++- 7 files changed, 258 insertions(+), 37 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs index 0487162a3..76418c68b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -13,7 +13,7 @@ use crate::prelude::*; #[derive(Debug, uniffi::Object)] pub struct SecurityShieldBuilder { - wrapped: RwLock>, + wrapped: RwLock, name: RwLock, } @@ -24,33 +24,22 @@ pub struct SecurityStructureOfFactorSourceIds { } impl SecurityShieldBuilder { - fn get( - &self, - with_non_consumed_builder: impl Fn(&MatrixBuilder) -> R, - ) -> R { + fn get(&self, access: impl Fn(&MatrixBuilder) -> R) -> R { let binding = self.wrapped.read().unwrap(); - - let Some(builder) = binding.as_ref() else { - unreachable!("Already built, should not have happened.") - }; - with_non_consumed_builder(builder) + access(&binding) } fn with>( &self, - mut with_non_consumed_builder: impl FnMut( - &mut MatrixBuilder, - ) -> Result, + mut write: impl FnMut(&mut MatrixBuilder) -> Result, ) -> Result { - let guard = self.wrapped.write(); - - let mut binding = guard.map_err(|_| CommonError::Unknown)?; // TODO: CommonError::MatrixBuilderRwLockPoisoned + let mut binding = self.wrapped.write().expect("No poison"); + write(&mut binding).map_err(|e| Into::::into(e)) + } - let Some(builder) = binding.as_mut() else { - return Err(CommonError::Unknown); // TODO: CommonError::AlreadyBuilt - }; - with_non_consumed_builder(builder) - .map_err(|e| Into::::into(e)) + fn set(&self, mut write: impl FnMut(&mut MatrixBuilder) -> R) -> R { + let mut binding = self.wrapped.write().expect("No poison"); + write(&mut binding) } fn validation_for_addition_of_factor_source_by_calling( @@ -87,7 +76,7 @@ impl SecurityShieldBuilder { #[uniffi::constructor] pub fn new() -> Arc { Arc::new(Self { - wrapped: RwLock::new(Some(MatrixBuilder::new())), + wrapped: RwLock::new(MatrixBuilder::new()), name: RwLock::new("My Shield".to_owned()), }) } @@ -142,6 +131,26 @@ impl SecurityShieldBuilder { } } +impl CommonError { + /// Checks if this CommonError (self) is a RoleBuilderValidation::NotYetValid violation, which + /// is in fact not a real error. + pub fn is_role_builder_not_yet_valid(&self) -> bool { + let internal = Into::::into(self.clone()); + internal.is_role_builder_not_yet_valid() + } + /// Checks if this CommonError (self) is a Matrix::NotYetValid violation, which + /// is in fact not a real error. + pub fn is_matrix_builder_not_yet_valid(&self) -> bool { + let internal = Into::::into(self.clone()); + internal.is_matrix_builder_not_yet_valid() + } + + pub fn is_not_yet_ready_violation(&self) -> bool { + self.is_role_builder_not_yet_valid() + || self.is_matrix_builder_not_yet_valid() + } +} + // ==================== // ===== MUTATION ===== // ==================== @@ -325,11 +334,7 @@ impl SecurityShieldBuilder { pub fn build( self: Arc, ) -> Result { - let mut binding = - self.wrapped.write().map_err(|_| CommonError::Unknown)?; // TODO: CommonError::MatrixBuilderRwLockPoisoned - let builder = binding.take().ok_or(CommonError::Unknown)?; // TODO: CommonError::AlreadyBuilt - let wrapped_matrix = - builder.build().map_err(|e| CommonError::Unknown)?; // TODO: CommonError::BuildError(format!("{:?}", e) + let wrapped_matrix = self.with(|builder| builder.build())?; let name = self.get_name(); let display_name = @@ -346,6 +351,74 @@ impl SecurityShieldBuilder { } } +/* + +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + enum_iterator::Sequence, + thiserror::Error, +)] +pub enum MatrixRolesInCombinationNotYetValid { + #[error("The single factor used in the primary role must not be used in any other role")] + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, +} + + +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + enum_iterator::Sequence, + thiserror::Error, +)] +pub enum NotYetValidReason { + #[error("Role must have at least one factor")] + RoleMustHaveAtLeastOneFactor, + + #[error( + "Primary role with password in threshold list must have another factor" + )] + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + + #[error( + "Primary role with threshold factors cannot have a threshold of zero" + )] + PrimaryRoleWithThresholdCannotBeZeroWithFactors, + + #[error("Primary role with password in threshold list must have threshold greater than one")] + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + + #[error("Threshold higher than threshold factors len")] + ThresholdHigherThanThresholdFactorsLen, +} +*/ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, uniffi::Enum)] +pub enum ShieldBuilderNotYetValid { + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, + + // ============= + // *** ROLES *** + // ============= + RoleMustHaveAtLeastOneFactor, + + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + + PrimaryRoleWithThresholdCannotBeZeroWithFactors, + + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + + ThresholdHigherThanThresholdFactorsLen, +} + impl FactorSourceID { pub fn new(inner: impl Borrow) -> Self { Self::from(*inner.borrow()) diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs index e7a181792..b65fe9a98 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs @@ -27,12 +27,41 @@ pub enum MatrixRolesInCombinationForeverInvalid { RecoveryAndConfirmationFactorsOverlap, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + enum_iterator::Sequence, + thiserror::Error, +)] pub enum MatrixRolesInCombinationNotYetValid { #[error("The single factor used in the primary role must not be used in any other role")] SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, } +impl MatrixRolesInCombinationNotYetValid { + pub fn all() -> Vec { + enum_iterator::all::().collect() + } +} + +impl CommonError { + /// Checks if this CommonError (self) is a MatrixBuilderValidation::MatrixRolesInCombinationNotYetValid violation, which + /// is in fact not a real error. + pub fn is_matrix_builder_not_yet_valid(&self) -> bool { + for not_yet_valid_reason in MatrixRolesInCombinationNotYetValid::all() { + let as_common_error = Self::from(not_yet_valid_reason); + if *self == as_common_error { + return true; + } + } + false + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] pub enum MatrixBuilderValidation { #[error("Role {role:?} in isolation violation: {violation}")] diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index dc3e814c0..4fe7120f6 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -30,7 +30,7 @@ impl MatrixBuilder { } } - pub fn build(self) -> MatrixBuilderBuildResult { + pub fn build(&self) -> MatrixBuilderBuildResult { self.validate_combination()?; let primary = self @@ -223,7 +223,68 @@ impl MatrixBuilder { .set_threshold(threshold) .into_matrix_err(RoleKind::Primary) } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ShieldBuilderNotYetValid { + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, + + // ============= + // *** ROLES *** + // ============= + RoleMustHaveAtLeastOneFactor, + + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + + PrimaryRoleWithThresholdCannotBeZeroWithFactors, + + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + + ThresholdHigherThanThresholdFactorsLen, +} +pub trait IntoShieldBuilderNotYetValid { + fn into_shield_builder_not_yet_valid( + self, + ) -> Option; +} + +// pub type MatrixBuilderMutateResult = Result<(), MatrixBuilderValidation>; +impl IntoShieldBuilderNotYetValid for MatrixBuilderValidation { + fn into_shield_builder_not_yet_valid( + self, + ) -> Option { + match self { + Self::RoleInIsolation { role, violation } => { + match violation { + RoleBuilderValidation::BasicViolation(_) | RoleBuilderValidation::ForeverInvalid(_) => None, + RoleBuilderValidation::NotYetValid(not_yet) => { + match not_yet { + NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => Some(ShieldBuilderNotYetValid::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor), + NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors => Some(ShieldBuilderNotYetValid::PrimaryRoleWithThresholdCannotBeZeroWithFactors), + NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne => Some(ShieldBuilderNotYetValid::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne), + NotYetValidReason::RoleMustHaveAtLeastOneFactor => Some(ShieldBuilderNotYetValid::RoleMustHaveAtLeastOneFactor), + NotYetValidReason::ThresholdHigherThanThresholdFactorsLen => Some(ShieldBuilderNotYetValid::ThresholdHigherThanThresholdFactorsLen), + } + } + } + } + Self::CombinationViolation(combo) => { + match combo { + MatrixRolesInCombinationViolation::Basic(_) | MatrixRolesInCombinationViolation::ForeverInvalid(_)=> None, + MatrixRolesInCombinationViolation::NotYetValid( + not_yet, + ) => { + match not_yet { + MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole => Some(ShieldBuilderNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole), + } + } + } + } + } + } +} +impl MatrixBuilder { pub fn get_threshold(&self) -> u8 { self.primary_role.get_threshold() } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs index b1508e6f4..4ba012de0 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs @@ -1350,6 +1350,8 @@ mod shield_configs { FactorSourceID::sample_ledger(), ) .unwrap(); + let build0 = sut.build(); // build err + assert!(build0.is_err()); sut.set_threshold(2).unwrap(); // Recovery @@ -1357,10 +1359,6 @@ mod shield_configs { FactorSourceID::sample_device(), ) .unwrap(); - sut.add_factor_source_to_recovery_override( - FactorSourceID::sample_ledger(), - ) - .unwrap(); // Confirmation sut.add_factor_source_to_confirmation_override( @@ -1368,9 +1366,20 @@ mod shield_configs { ) .unwrap(); + let built0 = sut.build().unwrap(); + + // Recovery - re + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .unwrap(); + // Build assert!(sut.validate().is_ok()); let built = sut.build().unwrap(); + let built2 = sut.build().unwrap(); + assert_ne!(built0, built); // we changed recovery since! + assert_eq!(built2, built); pretty_assertions::assert_eq!( built, MatrixOfFactorSourceIds::with_roles( diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs index 2b9438a0e..560aadcd5 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs @@ -185,16 +185,21 @@ mod threshold_suite { fn remove_from_override_does_not_change_threshold() { let mut sut = make(); sut.add_factor_source_to_threshold(sample()).unwrap(); + let _ = sut.build(); // build should not mutate neither consume sut.add_factor_source_to_threshold(sample_other()).unwrap(); let fs = FactorSourceID::sample_arculus_other(); sut.add_factor_source_to_override(fs).unwrap(); + let _ = sut.build(); // build should not mutate neither consume sut.set_threshold(2).unwrap(); + let _ = sut.build(); // build should not mutate neither consume assert_eq!(sut.get_threshold(), 2); sut.remove_factor_source(&fs).unwrap(); assert_eq!(sut.get_threshold(), 2); let built = sut.build().unwrap(); + let built2 = sut.build().unwrap(); assert_eq!(built.get_threshold(), 2); + assert_eq!(built2, built); // can built many times assert_eq!(built.role(), RoleKind::Primary); diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 996036e4a..d11b98354 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -61,7 +61,16 @@ pub enum BasicViolation { ConfirmationCannotSetThreshold, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + enum_iterator::Sequence, + thiserror::Error, +)] pub enum NotYetValidReason { #[error("Role must have at least one factor")] RoleMustHaveAtLeastOneFactor, @@ -82,6 +91,27 @@ pub enum NotYetValidReason { #[error("Threshold higher than threshold factors len")] ThresholdHigherThanThresholdFactorsLen, } +impl NotYetValidReason { + pub fn all() -> Vec { + enum_iterator::all::().collect() + } +} + +impl CommonError { + /// Checks if this CommonError (self) is a RoleBuilderValidation::NotYetValid violation, which + /// is in fact not a real error. + pub fn is_role_builder_not_yet_valid(&self) -> bool { + // for role in RoleKind::all() { + for not_yet_valid_reason in NotYetValidReason::all() { + let as_common_error = Self::from(not_yet_valid_reason); + if *self == as_common_error { + return true; + } + } + // } + false + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] pub enum ForeverInvalidReason { @@ -347,7 +377,7 @@ impl RoleBuilder { impl RoleBuilder { pub(crate) fn build( - self, + &self, ) -> Result, RoleBuilderValidation> { self.validate().map(|_| { RoleWithFactorSourceIds::with_factors( diff --git a/crates/sargon/src/signing/petition_types/role_kind.rs b/crates/sargon/src/signing/petition_types/role_kind.rs index 9895b71b4..00bd4ad70 100644 --- a/crates/sargon/src/signing/petition_types/role_kind.rs +++ b/crates/sargon/src/signing/petition_types/role_kind.rs @@ -3,7 +3,17 @@ use crate::prelude::*; /// A discriminator for `***RoleWithFactor***` types. Especially useful for /// `GeneralRoleWithHierarchicalDeterministicFactorInstances` which holds /// the role it is used for. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[derive( + Debug, + Clone, + Copy, + Serialize, + Deserialize, + PartialEq, + Eq, + Hash, + enum_iterator::Sequence, +)] #[serde(rename_all = "camelCase")] pub enum RoleKind { /// The primary role of some matrix of factors @@ -13,7 +23,11 @@ pub enum RoleKind { /// The confirmation role of some matrix of factors Confirmation, } - +impl RoleKind { + pub fn all() -> Vec { + enum_iterator::all::().collect() + } +} impl HasSampleValues for RoleKind { fn sample() -> Self { RoleKind::Primary From 43b75bff6f927cec7e4e8a997dab44fd5f316027 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 12:10:49 +0100 Subject: [PATCH 21/70] [no ci] revert --- .../matrices/builder/error.rs | 31 +--------- .../matrices/builder/matrix_builder.rs | 61 ------------------- .../roles/builder/roles_builder.rs | 32 +--------- .../src/signing/petition_types/role_kind.rs | 18 +----- 4 files changed, 4 insertions(+), 138 deletions(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs index b65fe9a98..e7a181792 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/error.rs @@ -27,41 +27,12 @@ pub enum MatrixRolesInCombinationForeverInvalid { RecoveryAndConfirmationFactorsOverlap, } -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - Hash, - enum_iterator::Sequence, - thiserror::Error, -)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] pub enum MatrixRolesInCombinationNotYetValid { #[error("The single factor used in the primary role must not be used in any other role")] SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, } -impl MatrixRolesInCombinationNotYetValid { - pub fn all() -> Vec { - enum_iterator::all::().collect() - } -} - -impl CommonError { - /// Checks if this CommonError (self) is a MatrixBuilderValidation::MatrixRolesInCombinationNotYetValid violation, which - /// is in fact not a real error. - pub fn is_matrix_builder_not_yet_valid(&self) -> bool { - for not_yet_valid_reason in MatrixRolesInCombinationNotYetValid::all() { - let as_common_error = Self::from(not_yet_valid_reason); - if *self == as_common_error { - return true; - } - } - false - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] pub enum MatrixBuilderValidation { #[error("Role {role:?} in isolation violation: {violation}")] diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index 4fe7120f6..75db6f48e 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -223,68 +223,7 @@ impl MatrixBuilder { .set_threshold(threshold) .into_matrix_err(RoleKind::Primary) } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum ShieldBuilderNotYetValid { - SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, - - // ============= - // *** ROLES *** - // ============= - RoleMustHaveAtLeastOneFactor, - - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, - - PrimaryRoleWithThresholdCannotBeZeroWithFactors, - - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, - - ThresholdHigherThanThresholdFactorsLen, -} -pub trait IntoShieldBuilderNotYetValid { - fn into_shield_builder_not_yet_valid( - self, - ) -> Option; -} - -// pub type MatrixBuilderMutateResult = Result<(), MatrixBuilderValidation>; -impl IntoShieldBuilderNotYetValid for MatrixBuilderValidation { - fn into_shield_builder_not_yet_valid( - self, - ) -> Option { - match self { - Self::RoleInIsolation { role, violation } => { - match violation { - RoleBuilderValidation::BasicViolation(_) | RoleBuilderValidation::ForeverInvalid(_) => None, - RoleBuilderValidation::NotYetValid(not_yet) => { - match not_yet { - NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => Some(ShieldBuilderNotYetValid::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor), - NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors => Some(ShieldBuilderNotYetValid::PrimaryRoleWithThresholdCannotBeZeroWithFactors), - NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne => Some(ShieldBuilderNotYetValid::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne), - NotYetValidReason::RoleMustHaveAtLeastOneFactor => Some(ShieldBuilderNotYetValid::RoleMustHaveAtLeastOneFactor), - NotYetValidReason::ThresholdHigherThanThresholdFactorsLen => Some(ShieldBuilderNotYetValid::ThresholdHigherThanThresholdFactorsLen), - } - } - } - } - Self::CombinationViolation(combo) => { - match combo { - MatrixRolesInCombinationViolation::Basic(_) | MatrixRolesInCombinationViolation::ForeverInvalid(_)=> None, - MatrixRolesInCombinationViolation::NotYetValid( - not_yet, - ) => { - match not_yet { - MatrixRolesInCombinationNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole => Some(ShieldBuilderNotYetValid::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole), - } - } - } - } - } - } -} -impl MatrixBuilder { pub fn get_threshold(&self) -> u8 { self.primary_role.get_threshold() } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index d11b98354..95d18bf5a 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -61,16 +61,7 @@ pub enum BasicViolation { ConfirmationCannotSetThreshold, } -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - Hash, - enum_iterator::Sequence, - thiserror::Error, -)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] pub enum NotYetValidReason { #[error("Role must have at least one factor")] RoleMustHaveAtLeastOneFactor, @@ -91,27 +82,6 @@ pub enum NotYetValidReason { #[error("Threshold higher than threshold factors len")] ThresholdHigherThanThresholdFactorsLen, } -impl NotYetValidReason { - pub fn all() -> Vec { - enum_iterator::all::().collect() - } -} - -impl CommonError { - /// Checks if this CommonError (self) is a RoleBuilderValidation::NotYetValid violation, which - /// is in fact not a real error. - pub fn is_role_builder_not_yet_valid(&self) -> bool { - // for role in RoleKind::all() { - for not_yet_valid_reason in NotYetValidReason::all() { - let as_common_error = Self::from(not_yet_valid_reason); - if *self == as_common_error { - return true; - } - } - // } - false - } -} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)] pub enum ForeverInvalidReason { diff --git a/crates/sargon/src/signing/petition_types/role_kind.rs b/crates/sargon/src/signing/petition_types/role_kind.rs index 00bd4ad70..9895b71b4 100644 --- a/crates/sargon/src/signing/petition_types/role_kind.rs +++ b/crates/sargon/src/signing/petition_types/role_kind.rs @@ -3,17 +3,7 @@ use crate::prelude::*; /// A discriminator for `***RoleWithFactor***` types. Especially useful for /// `GeneralRoleWithHierarchicalDeterministicFactorInstances` which holds /// the role it is used for. -#[derive( - Debug, - Clone, - Copy, - Serialize, - Deserialize, - PartialEq, - Eq, - Hash, - enum_iterator::Sequence, -)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "camelCase")] pub enum RoleKind { /// The primary role of some matrix of factors @@ -23,11 +13,7 @@ pub enum RoleKind { /// The confirmation role of some matrix of factors Confirmation, } -impl RoleKind { - pub fn all() -> Vec { - enum_iterator::all::().collect() - } -} + impl HasSampleValues for RoleKind { fn sample() -> Self { RoleKind::Primary From cf500046e488398ce86a13d2fc28993c81b32825 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 13:14:18 +0100 Subject: [PATCH 22/70] understandable generic names --- .../mfa/security_structures/builder.rs | 88 ------------------- .../matrices/matrix_of_factor_source_ids.rs | 1 - ...ource_in_role_builder_validation_status.rs | 2 +- .../abstract_matrix_builder_or_built.rs | 52 +++++++---- .../matrices/builder/matrix_builder.rs | 3 +- .../matrices/builder/matrix_template.rs | 4 +- .../roles/abstract_role_builder_or_built.rs | 45 +++++----- .../roles/builder/roles_builder.rs | 63 +++++++------ .../role_with_factor_instances.rs | 22 +++-- .../roles_with_factor_ids.rs | 4 +- .../factor_source_kind_level/role_template.rs | 4 +- .../roles_with_factor_sources.rs | 8 +- .../abstract_security_structure_of_factors.rs | 16 ++-- .../security_structure_of_factor_sources.rs | 6 +- 14 files changed, 135 insertions(+), 183 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs index 76418c68b..7cabb11d3 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs @@ -131,26 +131,6 @@ impl SecurityShieldBuilder { } } -impl CommonError { - /// Checks if this CommonError (self) is a RoleBuilderValidation::NotYetValid violation, which - /// is in fact not a real error. - pub fn is_role_builder_not_yet_valid(&self) -> bool { - let internal = Into::::into(self.clone()); - internal.is_role_builder_not_yet_valid() - } - /// Checks if this CommonError (self) is a Matrix::NotYetValid violation, which - /// is in fact not a real error. - pub fn is_matrix_builder_not_yet_valid(&self) -> bool { - let internal = Into::::into(self.clone()); - internal.is_matrix_builder_not_yet_valid() - } - - pub fn is_not_yet_ready_violation(&self) -> bool { - self.is_role_builder_not_yet_valid() - || self.is_matrix_builder_not_yet_valid() - } -} - // ==================== // ===== MUTATION ===== // ==================== @@ -351,74 +331,6 @@ impl SecurityShieldBuilder { } } -/* - -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - Hash, - enum_iterator::Sequence, - thiserror::Error, -)] -pub enum MatrixRolesInCombinationNotYetValid { - #[error("The single factor used in the primary role must not be used in any other role")] - SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, -} - - -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - Hash, - enum_iterator::Sequence, - thiserror::Error, -)] -pub enum NotYetValidReason { - #[error("Role must have at least one factor")] - RoleMustHaveAtLeastOneFactor, - - #[error( - "Primary role with password in threshold list must have another factor" - )] - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, - - #[error( - "Primary role with threshold factors cannot have a threshold of zero" - )] - PrimaryRoleWithThresholdCannotBeZeroWithFactors, - - #[error("Primary role with password in threshold list must have threshold greater than one")] - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, - - #[error("Threshold higher than threshold factors len")] - ThresholdHigherThanThresholdFactorsLen, -} -*/ - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, uniffi::Enum)] -pub enum ShieldBuilderNotYetValid { - SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, - - // ============= - // *** ROLES *** - // ============= - RoleMustHaveAtLeastOneFactor, - - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, - - PrimaryRoleWithThresholdCannotBeZeroWithFactors, - - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, - - ThresholdHigherThanThresholdFactorsLen, -} - impl FactorSourceID { pub fn new(inner: impl Borrow) -> Self { Self::from(*inner.borrow()) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 3118a60ca..52ab714fb 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -34,4 +34,3 @@ export_sample_config!(60); export_sample_config!(70); export_sample_config!(80); export_sample_config!(90); - diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 157af579d..244d57804 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -14,7 +14,7 @@ impl FactorSourceValidationStatus { .validation .map_err(|e| Into::::into((self.role, e))) { - return Some(e); + Some(e) } else { None } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs index 67e911ec3..cb99083cf 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs @@ -1,3 +1,5 @@ +#![allow(non_camel_case_types)] + use crate::prelude::*; /// Either a matrix or a **builder of a matrix** with a Primary, Recovery and Confirmation @@ -14,21 +16,24 @@ use crate::prelude::*; /// it is `PhantomData`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct AbstractMatrixBuilderOrBuilt { +pub struct AbstractMatrixBuilderOrBuilt { #[serde(skip)] #[doc(hidden)] - pub(crate) built: PhantomData, + pub(crate) built: PhantomData, - pub(crate) primary_role: AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, F, U>, + pub(crate) primary_role: + AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, FACTOR, BUILT_ROLE>, pub(crate) recovery_role: - AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, F, U>, + AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, FACTOR, BUILT_ROLE>, pub(crate) confirmation_role: - AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, F, U>, + AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, FACTOR, BUILT_ROLE>, pub number_of_days_until_auto_confirm: u16, } -impl AbstractMatrixBuilderOrBuilt { +impl + AbstractMatrixBuilderOrBuilt +{ pub const DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM: u16 = 14; /// # Safety @@ -36,9 +41,21 @@ impl AbstractMatrixBuilderOrBuilt { /// of unsafe - as in application **unsecure** - MatrixofFactors, which might /// lead to increase risk for end user to loose funds. pub unsafe fn unbuilt_with_roles_and_days( - primary: AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, F, U>, - recovery: AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, F, U>, - confirmation: AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, F, U>, + primary: AbstractRoleBuilderOrBuilt< + { ROLE_PRIMARY }, + FACTOR, + BUILT_ROLE, + >, + recovery: AbstractRoleBuilderOrBuilt< + { ROLE_RECOVERY }, + FACTOR, + BUILT_ROLE, + >, + confirmation: AbstractRoleBuilderOrBuilt< + { ROLE_CONFIRMATION }, + FACTOR, + BUILT_ROLE, + >, number_of_days_until_auto_confirm: u16, ) -> Self { Self { @@ -51,28 +68,31 @@ impl AbstractMatrixBuilderOrBuilt { } } -pub type AbstractMatrixBuilt = AbstractMatrixBuilderOrBuilt; +pub type AbstractMatrixBuilt = + AbstractMatrixBuilderOrBuilt; -impl AbstractMatrixBuilt { - pub fn primary(&self) -> &AbstractBuiltRoleWithFactor<{ ROLE_PRIMARY }, F> { +impl AbstractMatrixBuilt { + pub fn primary( + &self, + ) -> &AbstractBuiltRoleWithFactor<{ ROLE_PRIMARY }, FACTOR> { &self.primary_role } pub fn recovery( &self, - ) -> &AbstractBuiltRoleWithFactor<{ ROLE_RECOVERY }, F> { + ) -> &AbstractBuiltRoleWithFactor<{ ROLE_RECOVERY }, FACTOR> { &self.recovery_role } pub fn confirmation( &self, - ) -> &AbstractBuiltRoleWithFactor<{ ROLE_CONFIRMATION }, F> { + ) -> &AbstractBuiltRoleWithFactor<{ ROLE_CONFIRMATION }, FACTOR> { &self.confirmation_role } } -impl AbstractMatrixBuilt { - pub fn all_factors(&self) -> HashSet<&F> { +impl AbstractMatrixBuilt { + pub fn all_factors(&self) -> HashSet<&FACTOR> { let mut factors = HashSet::new(); factors.extend(self.primary_role.all_factors()); factors.extend(self.recovery_role.all_factors()); diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index 75db6f48e..0aa6a0ac1 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -6,13 +6,14 @@ pub type MatrixBuilderMutateResult = Result<(), MatrixBuilderValidation>; pub type MatrixBuilderBuildResult = Result; +/// Marker to distinguish between built and builder. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Built; pub type MatrixBuilder = AbstractMatrixBuilderOrBuilt< FactorSourceID, MatrixOfFactorSourceIds, - Built, // this is HACKY + Built, // Marker to distinguish between built and builder. >; // ================== diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs index 93ea6aa63..0ff1c7410 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -8,13 +8,13 @@ use crate::prelude::*; /// materialization. pub type MatrixTemplate = AbstractMatrixBuilt; -impl AbstractBuiltRoleWithFactor { +impl AbstractBuiltRoleWithFactor { /// Tries to materialize a RoleWithFactorSourceIds from a RoleTemplate by /// assigning each template with a concrete FactorSourceID using the FactorSourceIdAssigner. pub(crate) fn assign( self, factor_source_id_assigner: &mut FactorSourceIdAssigner, - ) -> Result, CommonError> { + ) -> Result, CommonError> { let mut fulfill = |xs: &Vec| -> Result, CommonError> { xs.iter() diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs index 815dc6fa4..94682efe9 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -29,10 +29,10 @@ use crate::prelude::*; /// * ConfirmationRoleWithFactorInstances #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct AbstractRoleBuilderOrBuilt { +pub struct AbstractRoleBuilderOrBuilt { #[serde(skip)] #[doc(hidden)] - built: PhantomData, + built: PhantomData, /// How many threshold factors that must be used to perform some function with /// this role. @@ -40,24 +40,25 @@ pub struct AbstractRoleBuilderOrBuilt { /// Factors which are used in combination with other factors, amounting to at /// least `threshold` many factors to perform some function with this role. - threshold_factors: Vec, + threshold_factors: Vec, /// Overriding / Super admin / "sudo" / God / factors, **ANY** /// single of these factor which can perform the function of this role, /// disregarding of `threshold`. - override_factors: Vec, + override_factors: Vec, } -pub(crate) type AbstractBuiltRoleWithFactor = - AbstractRoleBuilderOrBuilt; -pub(crate) type RoleBuilder = - AbstractRoleBuilderOrBuilt; +pub(crate) type AbstractBuiltRoleWithFactor = + AbstractRoleBuilderOrBuilt; -impl - AbstractRoleBuilderOrBuilt +pub(crate) type RoleBuilder = + AbstractRoleBuilderOrBuilt; + +impl + AbstractRoleBuilderOrBuilt { pub fn role(&self) -> RoleKind { - RoleKind::from_u8(R).expect("RoleKind should be valid") + RoleKind::from_u8(ROLE).expect("RoleKind should be valid") } /// # Safety @@ -66,11 +67,11 @@ impl /// lead to increase risk for end user to loose funds. pub unsafe fn unbuilt_with_factors( threshold: u8, - threshold_factors: impl IntoIterator, - override_factors: impl IntoIterator, + threshold_factors: impl IntoIterator, + override_factors: impl IntoIterator, ) -> Self { let assert_is_securified = - |factors: &Vec| -> Result<(), CommonError> { + |factors: &Vec| -> Result<(), CommonError> { let trait_objects: Vec<&dyn IsMaybeKeySpaceAware> = factors .iter() .map(|x| x as &dyn IsMaybeKeySpaceAware) @@ -105,8 +106,8 @@ impl pub(crate) fn with_factors( threshold: u8, - threshold_factors: impl IntoIterator, - override_factors: impl IntoIterator, + threshold_factors: impl IntoIterator, + override_factors: impl IntoIterator, ) -> Self { unsafe { Self::unbuilt_with_factors( @@ -118,9 +119,11 @@ impl } } -impl AbstractRoleBuilderOrBuilt { +impl + AbstractRoleBuilderOrBuilt +{ /// Threshold and Override factors mixed (threshold first). - pub fn all_factors(&self) -> Vec<&F> { + pub fn all_factors(&self) -> Vec<&FACTOR> { self.threshold_factors .iter() .chain(self.override_factors.iter()) @@ -129,14 +132,14 @@ impl AbstractRoleBuilderOrBuilt { /// Factors which are used in combination with other factors, amounting to at /// least `threshold` many factors to perform some function with this role. - pub fn get_threshold_factors(&self) -> &Vec { + pub fn get_threshold_factors(&self) -> &Vec { &self.threshold_factors } /// Overriding / Super admin / "sudo" / God / factors, **ANY** /// single of these factor which can perform the function of this role, /// disregarding of `threshold`. - pub fn get_override_factors(&self) -> &Vec { + pub fn get_override_factors(&self) -> &Vec { &self.override_factors } @@ -166,7 +169,7 @@ impl RoleFromDiscriminator for RoleKind { } } -impl RoleBuilder { +impl RoleBuilder { pub(crate) fn new() -> Self { Self { built: PhantomData, diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 95d18bf5a..036d818b2 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -224,9 +224,9 @@ pub enum Assert {} pub trait IsTrue {} impl IsTrue for Assert {} -impl RoleBuilder +impl RoleBuilder where - Assert<{ R == ROLE_PRIMARY }>: IsTrue, + Assert<{ ROLE == ROLE_PRIMARY }>: IsTrue, { /// If Ok => self is mutated /// If Err(NotYetValid) => self is mutated @@ -257,9 +257,9 @@ where } } -impl RoleBuilder +impl RoleBuilder where - Assert<{ R > ROLE_PRIMARY }>: IsTrue, + Assert<{ ROLE > ROLE_PRIMARY }>: IsTrue, { /// If Ok => self is mutated /// If Err(NotYetValid) => self is mutated @@ -272,7 +272,7 @@ where } } -impl RoleBuilder { +impl RoleBuilder { /// If Ok => self is mutated /// If Err(NotYetValid) => self is mutated /// If Err(ForeverInvalid) => self is not mutated @@ -345,10 +345,10 @@ impl RoleBuilder { } } -impl RoleBuilder { +impl RoleBuilder { pub(crate) fn build( &self, - ) -> Result, RoleBuilderValidation> { + ) -> Result, RoleBuilderValidation> { self.validate().map(|_| { RoleWithFactorSourceIds::with_factors( self.get_threshold(), @@ -366,7 +366,7 @@ impl RoleBuilder { match self.role() { Primary => { self.unchecked_set_threshold(threshold); - self.validate() + self.validate_threshold_for_primary() } Recovery => RoleBuilderMutateResult::basic_violation( RecoveryCannotSetThreshold, @@ -409,6 +409,32 @@ impl RoleBuilder { .any(|f| f.get_factor_source_kind() == factor_source_kind) } + pub(crate) fn check_threshold_for_primary( + &self, + ) -> Option { + if self.get_threshold_factors().len() < self.get_threshold() as usize { + return Some( + NotYetValidReason::ThresholdHigherThanThresholdFactorsLen, + ); + } + if self.get_threshold() == 0 && !self.get_threshold_factors().is_empty() + { + return Some( + NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors, + ); + } + None + } + + pub(crate) fn validate_threshold_for_primary( + &self, + ) -> RoleBuilderMutateResult { + if let Some(not_yet_valid) = self.check_threshold_for_primary() { + return Err(RoleBuilderValidation::NotYetValid(not_yet_valid)); + } + Ok(()) + } + /// Validates `self` by "replaying" the addition of each factor source in `self` to a /// "simulation" (clone). If the simulation is valid, then `self` is valid. pub(crate) fn validate(&self) -> RoleBuilderMutateResult { @@ -439,20 +465,7 @@ impl RoleBuilder { // Validate threshold count if self.role() == RoleKind::Primary { - if self.get_threshold_factors().len() - < self.get_threshold() as usize - { - return RoleBuilderMutateResult::not_yet_valid( - NotYetValidReason::ThresholdHigherThanThresholdFactorsLen, - ); - } - if self.get_threshold() == 0 - && !self.get_threshold_factors().is_empty() - { - return RoleBuilderMutateResult::not_yet_valid( - NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors, - ); - } + self.validate_threshold_for_primary()?; } else if self.get_threshold() != 0 { match self.role() { Primary => unreachable!( @@ -679,7 +692,7 @@ impl RoleBuilder { // ======================= // ======== RULES ======== // ======================= -impl RoleBuilder { +impl RoleBuilder { fn validation_for_addition_of_password_to_primary( &self, factor_list_kind: FactorListKind, @@ -734,8 +747,8 @@ impl RoleBuilder { } #[cfg(test)] -pub(crate) fn test_duplicates_not_allowed( - sut: RoleBuilder, +pub(crate) fn test_duplicates_not_allowed( + sut: RoleBuilder, list: FactorListKind, factor_source_id: FactorSourceID, ) { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs index 978b98818..d8c8e5521 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs @@ -1,10 +1,12 @@ use crate::prelude::*; -pub(crate) type RoleWithFactorInstances = - AbstractBuiltRoleWithFactor; +pub(crate) type RoleWithFactorInstances = + AbstractBuiltRoleWithFactor; -impl RoleWithFactorSources { - fn from(other: &RoleWithFactorSources) -> Self { +impl RoleWithFactorSources { + fn from( + other: &RoleWithFactorSources, + ) -> Self { Self::with_factors( other.get_threshold(), other.get_threshold_factors().clone(), @@ -14,8 +16,10 @@ impl RoleWithFactorSources { } impl MatrixOfFactorSources { - pub(crate) fn get_role(&self) -> RoleWithFactorSources { - match R { + pub(crate) fn get_role( + &self, + ) -> RoleWithFactorSources { + match ROLE { ROLE_PRIMARY => RoleWithFactorSources::from(&self.primary_role), ROLE_RECOVERY => RoleWithFactorSources::from(&self.recovery_role), ROLE_CONFIRMATION => { @@ -26,14 +30,14 @@ impl MatrixOfFactorSources { } } -impl RoleWithFactorInstances { +impl RoleWithFactorInstances { pub(crate) fn fulfilling_role_of_factor_sources_with_factor_instances( consuming_instances: &IndexMap, matrix_of_factor_sources: &MatrixOfFactorSources, ) -> Result { - let role_kind = RoleKind::from_u8(R).unwrap(); + let role_kind = RoleKind::from_u8(ROLE).unwrap(); - let role_of_sources = matrix_of_factor_sources.get_role::(); + let role_of_sources = matrix_of_factor_sources.get_role::(); assert_eq!(role_of_sources.role(), role_kind); let threshold: u8 = role_of_sources.get_threshold(); diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs index fd2d723da..8ddab0052 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs @@ -1,4 +1,4 @@ use crate::prelude::*; -pub type RoleWithFactorSourceIds = - AbstractBuiltRoleWithFactor; +pub type RoleWithFactorSourceIds = + AbstractBuiltRoleWithFactor; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs index 54524c7f5..a183eb2dc 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_kind_level/role_template.rs @@ -14,8 +14,8 @@ pub struct FactorSourceTemplate { pub id: u8, } -pub(crate) type RoleTemplate = - AbstractBuiltRoleWithFactor; +pub(crate) type RoleTemplate = + AbstractBuiltRoleWithFactor; pub type PrimaryRoleTemplate = RoleTemplate<{ ROLE_PRIMARY }>; pub type RecoveryRoleTemplate = RoleTemplate<{ ROLE_RECOVERY }>; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs index a34d8cd00..0a664f3b5 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs @@ -1,11 +1,11 @@ use crate::prelude::*; -pub(crate) type RoleWithFactorSources = - AbstractBuiltRoleWithFactor; +pub(crate) type RoleWithFactorSources = + AbstractBuiltRoleWithFactor; -impl RoleWithFactorSources { +impl RoleWithFactorSources { pub fn new( - role_with_factor_source_ids: RoleWithFactorSourceIds, + role_with_factor_source_ids: RoleWithFactorSourceIds, factor_sources: &FactorSources, ) -> Result { let lookup_f = diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs index 06d9c15f6..9ba771767 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/abstract_security_structure_of_factors.rs @@ -2,17 +2,17 @@ use crate::prelude::*; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct AbstractSecurityStructure { +pub struct AbstractSecurityStructure { /// Metadata of this Security Structure, such as globally unique and /// stable identifier, creation date and user chosen label (name). pub metadata: SecurityStructureMetadata, /// The structure of factors to use for certain roles, Primary, Recovery /// and Confirmation role. - pub matrix_of_factors: AbstractMatrixBuilt, + pub matrix_of_factors: AbstractMatrixBuilt, } -impl Identifiable for AbstractSecurityStructure { +impl Identifiable for AbstractSecurityStructure { type ID = ::ID; fn id(&self) -> Self::ID { @@ -20,16 +20,16 @@ impl Identifiable for AbstractSecurityStructure { } } -impl AbstractSecurityStructure { - pub fn all_factors(&self) -> HashSet<&F> { +impl AbstractSecurityStructure { + pub fn all_factors(&self) -> HashSet<&FACTOR> { self.matrix_of_factors.all_factors() } } -impl AbstractSecurityStructure { +impl AbstractSecurityStructure { pub fn with_metadata( metadata: SecurityStructureMetadata, - matrix_of_factors: AbstractMatrixBuilt, + matrix_of_factors: AbstractMatrixBuilt, ) -> Self { Self { metadata, @@ -39,7 +39,7 @@ impl AbstractSecurityStructure { pub fn new( display_name: DisplayName, - matrix_of_factors: AbstractMatrixBuilt, + matrix_of_factors: AbstractMatrixBuilt, ) -> Self { let metadata = SecurityStructureMetadata::new(display_name); Self::with_metadata(metadata, matrix_of_factors) diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs index 6fe37ccce..ec457b5ee 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs @@ -58,10 +58,10 @@ impl From } } -impl From> - for AbstractRoleBuilderOrBuilt +impl From> + for AbstractRoleBuilderOrBuilt { - fn from(value: AbstractRoleBuilderOrBuilt) -> Self { + fn from(value: AbstractRoleBuilderOrBuilt) -> Self { Self::with_factors( value.get_threshold(), value From eab4b5e8511f727b51c12d094774882f05c0be82 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 13:57:28 +0100 Subject: [PATCH 23/70] [no ci] WIP --- .../SecurityShieldBuilder+Swiftiefied.swift | 29 +++++------- .../MFA/SecurityShieldsBuilderTests.swift | 44 +++++++++---------- .../roles/builder/roles_builder.rs | 2 +- 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift index 77b6521f0..6a0e9d031 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift @@ -1,28 +1,22 @@ -// -// SecurityShieldBuilder+Swiftiefied.swift -// Sargon -// -// Created by Alexander Cyon on 2024-12-02. -// - import SargonUniFFI extension FactorSourceValidationStatus { public var factorSourceID: FactorSourceID { factorSourceId() } - + public var validationError: CommonError? { self.validationErr() } - + public var role: RoleKind { role() } } + extension SecurityShieldBuilder { public typealias Factor = FactorSourceID - + /// Confirmation Role public var numberOfDaysUntilAutoConfirm: UInt16 { get { getNumberOfDaysUntilAutoConfirm() } @@ -31,27 +25,27 @@ extension SecurityShieldBuilder { try! setNumberOfDaysUntilAutoConfirm(numberOfDays: UInt16(newValue)) } } - + public var threshold: UInt8 { - getPrimaryThreshold() + getPrimaryThreshold() } - + public var primaryRoleThresholdFactors: [Factor] { getPrimaryThresholdFactors() } - + public var primaryRoleOverrideFactors: [Factor] { getPrimaryOverrideFactors() } - + public var recoveryRoleFactors: [Factor] { getRecoveryFactors() } - + public var confirmationRoleFactors: [Factor] { getConfirmationFactors() } - + /// Name of the shield public var name: String { get { @@ -61,5 +55,4 @@ extension SecurityShieldBuilder { setName(name: newValue) } } - } diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index 54b7ea48e..47b8f23cf 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -6,25 +6,23 @@ import Testing @Suite("ShieldBuilder") struct ShieldTests { + @Test("name") + func name() { + let builder = SecurityShieldBuilder() + #expect(builder.name == "My Shield") + builder.name = "S.H.I.E.L.D" + #expect(builder.name == "S.H.I.E.L.D") + } + + @Test("threshold") + func threshold() { + let builder = SecurityShieldBuilder() + #expect(builder.threshold == 0) + #expect(throws: CommonError.RoleMustHaveAtLeastOneFactor) { + try builder.setThreshold(threshold: 0) + } + } - @Test("name") - func name() { - let builder = SecurityShieldBuilder() - #expect(builder.name == "My Shield") - builder.name = "S.H.I.E.L.D" - #expect(builder.name == "S.H.I.E.L.D") - } - - - @Test("threshold") - func threshold() { - let builder = SecurityShieldBuilder() - #expect(builder.threshold == 0) - #expect(throws: CommonError.RoleMustHaveAtLeastOneFactor) { - try builder.setThreshold(threshold: 0) - } - } - @Test("days") func days() { let builder = SecurityShieldBuilder() @@ -33,31 +31,31 @@ struct ShieldTests { try builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 0) } } - + @Test("empty primary threshold") func emptyThresholdFactors() { let builder = SecurityShieldBuilder() #expect(builder.primaryRoleThresholdFactors == []) } - + @Test("empty primary override") func emptyOverrideFactors() { let builder = SecurityShieldBuilder() #expect(builder.primaryRoleOverrideFactors == []) } - + @Test("empty recovery") func emptyRecoveryFactors() { let builder = SecurityShieldBuilder() #expect(builder.recoveryRoleFactors == []) } - + @Test("empty confirmation") func emptyConfirmationFactors() { let builder = SecurityShieldBuilder() #expect(builder.confirmationRoleFactors == []) } - + @Test("primary override validation status trustedContact") func primValidationStatusTrustedContact() throws { let builder = SecurityShieldBuilder() diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 036d818b2..d72491660 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -366,7 +366,7 @@ impl RoleBuilder { match self.role() { Primary => { self.unchecked_set_threshold(threshold); - self.validate_threshold_for_primary() + self.validate() } Recovery => RoleBuilderMutateResult::basic_violation( RecoveryCannotSetThreshold, From b958ef17f288137d283105780da18d11f6345a92 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 15:24:28 +0100 Subject: [PATCH 24/70] [no ci] wip --- ...FileSystemDriver+Data+ContentsOf+URL.swift | 2 +- .../Logging/LoggingDriver+SwiftLog.swift | 2 +- .../ProfileStateChange.swift | 2 +- .../UnsafeStorageDriver+UserDefaults.swift | 2 +- .../BIP39/BIP39Language+Swiftified.swift | 2 +- .../BIP39/BIP39WordCount+Swiftified.swift | 6 +- .../Derivation/BIP39Word+Swiftified.swift | 4 +- .../Derivation/BIP44LikePath+Swiftified.swift | 4 +- .../Crypto/SLIP10Curve+Swiftified.swift | 8 +- .../Prelude/NetworkID+Swiftified.swift | 6 +- .../NonFungibleGlobalID+Swiftified.swift | 2 +- .../NonFungibleLocalID+Swiftified.swift | 4 +- .../NonFungibleLocalIDString+Swiftified.swift | 2 +- .../Prelude/SargonError+Swiftified.swift | 8 +- .../Account/AppearanceID+Swiftified.swift | 8 +- .../FactorSourceID+Swiftified.swift | 6 +- ...FactorSourceIDFromAddress+Swiftified.swift | 6 +- .../FactorSourceIDFromHash+Swiftified.swift | 6 +- .../P2PLinks/P2PLink+Swiftified.swift | 6 +- .../System/BIOS/BIOS+Swiftified.swift | 2 +- .../System/Drivers/Drivers+Swiftified.swift | 2 +- .../System/SargonOS+Swiftified.swift | 2 +- .../profile/mfa/security_structures/mod.rs | 2 +- ...{builder.rs => security_shield_builder.rs} | 5 +- .../profile/mfa/security_structures/mod.rs | 2 + .../roles/builder/roles_builder.rs | 8 +- .../security_shield_builder.rs | 652 ++++++++++++++++++ .../Planbok/Dependencies/Keychain.swift | 2 +- .../Features/OverlayWindow/HUDFeature.swift | 2 +- .../Planbok/SharedState/SargonKey.swift | 4 +- 30 files changed, 711 insertions(+), 58 deletions(-) rename crates/sargon-uniffi/src/profile/mfa/security_structures/{builder.rs => security_shield_builder.rs} (99%) create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs diff --git a/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift b/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift index 09cc9cd34..8d380bb38 100644 --- a/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift +++ b/apple/Sources/Sargon/Drivers/FileSystem/FileSystemDriver+Data+ContentsOf+URL.swift @@ -1,7 +1,7 @@ import Foundation import SargonUniFFI -// MARK: - FileManager + Sendable +// MARK: - FileManager + @unchecked Sendable extension FileManager: @unchecked Sendable {} // Makes it possible to type `.shared` on an initalizer/func taking diff --git a/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift b/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift index 31a007fb1..f5ee3b423 100644 --- a/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift +++ b/apple/Sources/Sargon/Drivers/Logging/LoggingDriver+SwiftLog.swift @@ -100,7 +100,7 @@ extension LogLevel: CaseIterable { public static let allCases: [Self] = rustLoggerGetAllLevels() } -// MARK: - Logger + Sendable +// MARK: - Logger + @unchecked Sendable extension Logger: @unchecked Sendable {} extension OSLogType { diff --git a/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift b/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift index 5c6611dd8..5dbc3b612 100644 --- a/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift +++ b/apple/Sources/Sargon/Drivers/ProfileStateChange/ProfileStateChange.swift @@ -2,7 +2,7 @@ import SargonUniFFI public typealias ProfileStateChangeEventPublisher = EventPublisher -// MARK: ProfileStateChangeDriver +// MARK: - ProfileStateChangeEventPublisher + ProfileStateChangeDriver extension ProfileStateChangeEventPublisher: ProfileStateChangeDriver { public static let shared = ProfileStateChangeEventPublisher() diff --git a/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift b/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift index 2b22bc171..00f29187a 100644 --- a/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift +++ b/apple/Sources/Sargon/Drivers/UnsafeStorage/UnsafeStorageDriver+UserDefaults.swift @@ -1,7 +1,7 @@ import Foundation import SargonUniFFI -// MARK: - UserDefaults + Sendable +// MARK: - UserDefaults + @unchecked Sendable extension UserDefaults: @unchecked Sendable {} // Makes it possible to type `.shared` on an initalizer/func taking diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift index 0b82fee5e..f4b5a5716 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift @@ -3,5 +3,5 @@ import SargonUniFFI public typealias BIP39Language = Bip39Language -// MARK: SargonModel +// MARK: - BIP39Language + SargonModel extension BIP39Language: SargonModel {} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift index e08d21239..1cc6527e1 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias BIP39WordCount = Bip39WordCount -// MARK: SargonModel +// MARK: - BIP39WordCount + SargonModel extension BIP39WordCount: SargonModel { public static let sample: Self = .twentyFour public static let sampleOther: Self = .twelve @@ -15,7 +15,7 @@ extension BIP39WordCount { } } -// MARK: Identifiable +// MARK: - BIP39WordCount + Identifiable extension BIP39WordCount: Identifiable { public typealias ID = RawValue public var id: ID { @@ -23,7 +23,7 @@ extension BIP39WordCount: Identifiable { } } -// MARK: Comparable +// MARK: - BIP39WordCount + Comparable extension BIP39WordCount: Comparable { public static func < (lhs: Self, rhs: Self) -> Bool { lhs.rawValue < rhs.rawValue diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift index 15010a9f0..dd0490d4a 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Word+Swiftified.swift @@ -2,10 +2,10 @@ import SargonUniFFI public typealias BIP39Word = Bip39Word -// MARK: SargonModel +// MARK: - BIP39Word + SargonModel extension BIP39Word: SargonModel {} -// MARK: Identifiable +// MARK: - BIP39Word + Identifiable extension BIP39Word: Identifiable { public typealias ID = U11 diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift index cba7536c6..8f4e854c9 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP44LikePath+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias BIP44LikePath = Bip44LikePath -// MARK: SargonModel, DerivationPathProtocol +// MARK: - BIP44LikePath + SargonModel, DerivationPathProtocol extension BIP44LikePath: SargonModel, DerivationPathProtocol { public var asGeneral: DerivationPath { .bip44Like(value: self) @@ -14,7 +14,7 @@ extension BIP44LikePath: SargonModel, DerivationPathProtocol { } } -// MARK: CustomStringConvertible +// MARK: - BIP44LikePath + CustomStringConvertible extension BIP44LikePath: CustomStringConvertible { public var description: String { toString() diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift index 04f03d82d..44748ee04 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/SLIP10Curve+Swiftified.swift @@ -2,10 +2,10 @@ import SargonUniFFI public typealias SLIP10Curve = Slip10Curve -// MARK: SargonModel +// MARK: - SLIP10Curve + SargonModel extension SLIP10Curve: SargonModel {} -// MARK: Identifiable +// MARK: - SLIP10Curve + Identifiable extension SLIP10Curve: Identifiable { public typealias ID = String public var id: ID { @@ -13,7 +13,7 @@ extension SLIP10Curve: Identifiable { } } -// MARK: CustomStringConvertible +// MARK: - SLIP10Curve + CustomStringConvertible extension SLIP10Curve: CustomStringConvertible { public var description: String { toString() @@ -26,5 +26,5 @@ extension SLIP10Curve { } } -// MARK: SargonStringCodable +// MARK: - SLIP10Curve + SargonStringCodable extension SLIP10Curve: SargonStringCodable {} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift index ee98052d7..0f6291801 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NetworkID+Swiftified.swift @@ -3,17 +3,17 @@ import SargonUniFFI public typealias NetworkID = NetworkId -// MARK: SargonModel +// MARK: - NetworkID + SargonModel extension NetworkID: SargonModel {} -// MARK: CustomStringConvertible +// MARK: - NetworkID + CustomStringConvertible extension NetworkID: CustomStringConvertible { public var description: String { toString() } } -// MARK: Codable +// MARK: - NetworkID + Codable extension NetworkID: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift index a0bad729e..fb38bc221 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleGlobalID+Swiftified.swift @@ -2,7 +2,7 @@ import SargonUniFFI public typealias NonFungibleGlobalID = NonFungibleGlobalId -// MARK: IdentifiableByStringProtocol +// MARK: - NonFungibleGlobalID + IdentifiableByStringProtocol extension NonFungibleGlobalID: IdentifiableByStringProtocol { public var localID: NonFungibleLocalID { nonFungibleLocalId diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift index ba577a6ea..882ad6428 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalID+Swiftified.swift @@ -3,10 +3,10 @@ import SargonUniFFI public typealias NonFungibleLocalID = NonFungibleLocalId -// MARK: IdentifiableByStringProtocol +// MARK: - NonFungibleLocalID + IdentifiableByStringProtocol extension NonFungibleLocalID: IdentifiableByStringProtocol {} -// MARK: ExpressibleByIntegerLiteral +// MARK: - NonFungibleLocalID + ExpressibleByIntegerLiteral extension NonFungibleLocalID: ExpressibleByIntegerLiteral { public init(integerLiteral value: UInt64) { self.init(integer: value) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift index 193307ec8..73e1e6489 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/NonFungibleLocalIDString+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias NonFungibleLocalIDString = NonFungibleLocalIdString -// MARK: SargonModel +// MARK: - NonFungibleLocalIDString + SargonModel extension NonFungibleLocalIDString: SargonModel {} #if DEBUG diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift index 2cc184abf..8ad3267ff 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Prelude/SargonError+Swiftified.swift @@ -3,24 +3,24 @@ import SargonUniFFI public typealias SargonError = CommonError -// MARK: SargonModel +// MARK: - SargonError + SargonModel extension SargonError: SargonModel {} -// MARK: CustomDebugStringConvertible +// MARK: - SargonError + CustomDebugStringConvertible extension SargonError: CustomDebugStringConvertible { public var debugDescription: String { "\(errorCode): \(errorMessage)" } } -// MARK: CustomStringConvertible +// MARK: - SargonError + CustomStringConvertible extension SargonError: CustomStringConvertible { public var description: String { errorMessage } } -// MARK: LocalizedError +// MARK: - SargonError + LocalizedError extension SargonError: LocalizedError { public var errorDescription: String? { let errorCodeFormatted = "Error code: \(errorCode)" diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift index 39a8ab7be..acc2441b5 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Account/AppearanceID+Swiftified.swift @@ -2,10 +2,10 @@ import SargonUniFFI public typealias AppearanceID = AppearanceId -// MARK: SargonModel +// MARK: - AppearanceID + SargonModel extension AppearanceID: SargonModel {} -// MARK: Identifiable +// MARK: - AppearanceID + Identifiable extension AppearanceID: Identifiable { public typealias ID = UInt8 public var id: ID { @@ -13,14 +13,14 @@ extension AppearanceID: Identifiable { } } -// MARK: CustomStringConvertible +// MARK: - AppearanceID + CustomStringConvertible extension AppearanceID: CustomStringConvertible { public var description: String { value.description } } -// MARK: Codable +// MARK: - AppearanceID + Codable extension AppearanceID: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift index d60b959ae..481b3f09d 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceID+Swiftified.swift @@ -3,17 +3,17 @@ import SargonUniFFI public typealias FactorSourceID = FactorSourceId -// MARK: SargonModel +// MARK: - FactorSourceID + SargonModel extension FactorSourceID: SargonModel {} -// MARK: CustomStringConvertible +// MARK: - FactorSourceID + CustomStringConvertible extension FactorSourceID: CustomStringConvertible { public var description: String { toString() } } -// MARK: FactorSourceIDProtocol +// MARK: - FactorSourceID + FactorSourceIDProtocol extension FactorSourceID: FactorSourceIDProtocol { public var asGeneral: FactorSourceID { self diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift index 9a15a05b6..70232feb7 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromAddress+Swiftified.swift @@ -3,13 +3,13 @@ import SargonUniFFI public typealias FactorSourceIDFromAddress = FactorSourceIdFromAddress -// MARK: SargonModel +// MARK: - FactorSourceIDFromAddress + SargonModel extension FactorSourceIDFromAddress: SargonModel {} -// MARK: SargonObjectCodable +// MARK: - FactorSourceIDFromAddress + SargonObjectCodable extension FactorSourceIDFromAddress: SargonObjectCodable {} -// MARK: FactorSourceIDSpecificProtocol +// MARK: - FactorSourceIDFromAddress + FactorSourceIDSpecificProtocol extension FactorSourceIDFromAddress: FactorSourceIDSpecificProtocol { public var asGeneral: FactorSourceID { .address(value: self) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift index 59a69fb07..0070f7ff3 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceIDFromHash+Swiftified.swift @@ -3,13 +3,13 @@ import SargonUniFFI public typealias FactorSourceIDFromHash = FactorSourceIdFromHash -// MARK: SargonModel +// MARK: - FactorSourceIDFromHash + SargonModel extension FactorSourceIDFromHash: SargonModel {} -// MARK: SargonObjectCodable +// MARK: - FactorSourceIDFromHash + SargonObjectCodable extension FactorSourceIDFromHash: SargonObjectCodable {} -// MARK: FactorSourceIDSpecificProtocol +// MARK: - FactorSourceIDFromHash + FactorSourceIDSpecificProtocol extension FactorSourceIDFromHash: FactorSourceIDSpecificProtocol { public var asGeneral: FactorSourceID { .hash(value: self) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift index 876209393..004761de5 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/RadixConnect/P2PLinks/P2PLink+Swiftified.swift @@ -3,13 +3,13 @@ import SargonUniFFI public typealias P2PLink = P2pLink -// MARK: SargonModel +// MARK: - P2PLink + SargonModel extension P2PLink: SargonModel {} -// MARK: SargonObjectCodable +// MARK: - P2PLink + SargonObjectCodable extension P2PLink: SargonObjectCodable {} -// MARK: Identifiable +// MARK: - P2PLink + Identifiable extension P2PLink: Identifiable { public typealias ID = PublicKeyHash } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift index 49a19e1fe..82ba53526 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/System/BIOS/BIOS+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias BIOS = Bios -// MARK: Sendable +// MARK: - BIOS + @unchecked Sendable extension BIOS: @unchecked Sendable {} extension BIOS { diff --git a/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift index 6e033bdd2..5bfe9c472 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/System/Drivers/Drivers+Swiftified.swift @@ -1,7 +1,7 @@ import Foundation import SargonUniFFI -// MARK: - Drivers + Sendable +// MARK: - Drivers + @unchecked Sendable extension Drivers: @unchecked Sendable {} extension Drivers { diff --git a/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift index a902b7cde..703d15e7d 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/System/SargonOS+Swiftified.swift @@ -3,5 +3,5 @@ import SargonUniFFI public typealias SargonOS = SargonOs -// MARK: Sendable +// MARK: - SargonOS + @unchecked Sendable extension SargonOS: @unchecked Sendable {} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index 8ff99088d..1f32904c3 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,4 +1,4 @@ -mod builder; +mod security_shield_builder; mod error_conversion; mod matrices; mod models; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs similarity index 99% rename from crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs rename to crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 7cabb11d3..c165d0962 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -13,8 +13,7 @@ use crate::prelude::*; #[derive(Debug, uniffi::Object)] pub struct SecurityShieldBuilder { - wrapped: RwLock, - name: RwLock, + wrapped: RwLock, } #[derive(Debug, PartialEq, Eq, Hash, uniffi::Object)] @@ -312,7 +311,7 @@ impl SecurityShieldBuilder { } pub fn build( - self: Arc, + &self, ) -> Result { let wrapped_matrix = self.with(|builder| builder.build())?; diff --git a/crates/sargon/src/profile/mfa/security_structures/mod.rs b/crates/sargon/src/profile/mfa/security_structures/mod.rs index 0472c3fe4..3236bab04 100644 --- a/crates/sargon/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/mod.rs @@ -1,6 +1,7 @@ mod has_role_kind; mod matrices; mod roles; +mod security_shield_builder; mod security_structure_id; mod security_structure_metadata; mod security_structure_of_factors; @@ -8,6 +9,7 @@ mod security_structure_of_factors; pub use has_role_kind::*; pub use matrices::*; pub use roles::*; +pub use security_shield_builder::*; pub use security_structure_id::*; pub use security_structure_metadata::*; pub use security_structure_of_factors::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index d72491660..198a6dc1d 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -358,7 +358,6 @@ impl RoleBuilder { }) } - #[allow(dead_code)] pub(crate) fn set_threshold( &mut self, threshold: u8, @@ -512,7 +511,6 @@ impl RoleBuilder { } } - #[allow(dead_code)] /// For each factor source in the given set, return a validation status /// for adding it to factor list of the given kind (`factor_list_kind`) pub(crate) fn validation_for_addition_of_factor_source_for_each( @@ -592,8 +590,10 @@ impl RoleBuilder { if self.override_contains_factor_source(factor_source_id) { remove(self.mut_override_factors()) - } - if self.threshold_contains_factor_source(factor_source_id) { + } else if self.threshold_contains_factor_source(factor_source_id) { + // We use `else if` to highlight the fact that a factor cannot + // ever be in both override and threshold list. + remove(self.mut_threshold_factors()); let threshold_factors_len = self.get_threshold_factors().len() as u8; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs new file mode 100644 index 000000000..44d6304fb --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -0,0 +1,652 @@ +use crate::prelude::*; + +#[derive(Debug)] +pub struct SecurityShieldBuilder { + matrix_builder: RwLock, + name: RwLock, +} + +impl SecurityShieldBuilder { + fn get(&self, access: impl Fn(&MatrixBuilder) -> R) -> R { + let binding = self.matrix_builder.read().unwrap(); + access(&binding) + } + + fn with>( + &self, + mut write: impl FnMut(&mut MatrixBuilder) -> Result, + ) -> Result { + let mut binding = self.matrix_builder.write().expect("No poison"); + write(&mut binding).map_err(|e| Into::::into(e)) + } + + // Ignores error and returns a ref to self + fn set(&self, mut write: impl FnMut(&mut MatrixBuilder) -> R) -> &Self { + let mut binding = self.matrix_builder.write().expect("No poison"); + write(&mut binding); + self + } + + fn validation_for_addition_of_factor_source_by_calling( + &self, + factor_sources: Vec, + call: impl Fn( + &MatrixBuilder, + &IndexSet, + ) + -> IndexSet, + ) -> Vec { + // let input = &factor_sources + // .clone() + // .into_iter() + // .map(Into::::into) + // .collect::>(); + // self.with(|builder| { + // let xs = call(builder, input); + // Ok::<_, CommonError>(xs) + // }) + // .expect("No poison") + todo!() + } +} + +impl SecurityShieldBuilder { + fn get_factors( + &self, + access: impl Fn(&MatrixBuilder) -> &Vec, + ) -> Vec { + self.get(|builder| { + let factors = access(builder); + factors + .iter() + .map(|x| crate::FactorSourceID::from(*x)) + .collect::>() + }) + } +} + +// ==================== +// ==== GET / READ ==== +// ==================== +impl SecurityShieldBuilder { + pub fn get_primary_threshold(&self) -> u8 { + self.get(|builder| builder.get_threshold()) + } + + pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { + self.get(|builder| builder.get_number_of_days_until_auto_confirm()) + } + + pub fn get_name(&self) -> String { + self.name.read().unwrap().clone() + } + + pub fn get_primary_threshold_factors(&self) -> Vec { + self.get_factors(|builder| builder.get_primary_threshold_factors()) + } + + pub fn get_primary_override_factors(&self) -> Vec { + self.get_factors(|builder| builder.get_primary_override_factors()) + } + + pub fn get_recovery_factors(&self) -> Vec { + self.get_factors(|builder| builder.get_recovery_factors()) + } + + pub fn get_confirmation_factors(&self) -> Vec { + self.get_factors(|builder| builder.get_confirmation_factors()) + } +} + +// ==================== +// ===== MUTATION ===== +// ==================== +impl SecurityShieldBuilder { + pub fn set_name(&self, name: String) -> &Self { + *self.name.write().unwrap() = name; + self + } + + /// Adds the factor source to the primary role threshold list. + pub fn add_factor_source_to_primary_threshold( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.add_factor_source_to_primary_threshold( + factor_source_id.clone().into(), + ) + }) + } + + pub fn add_factor_source_to_primary_override( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.add_factor_source_to_primary_override( + factor_source_id.clone().into(), + ) + }) + } + + pub fn remove_factor( + &self, + factor_source_id: FactorSourceID, + ) -> Result<(), CommonError> { + self.with(|builder| { + builder.remove_factor(&factor_source_id.clone().into()) + }) + } + + pub fn set_threshold(&self, threshold: u8) -> &Self { + self.set(|builder| builder.set_threshold(threshold)) + } + + pub fn set_number_of_days_until_auto_confirm( + &self, + number_of_days: u16, + ) -> &Self { + self.set(|builder| { + builder.set_number_of_days_until_auto_confirm(number_of_days) + }) + } + + pub fn add_factor_source_to_recovery_override( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.add_factor_source_to_recovery_override( + factor_source_id.clone().into(), + ) + }) + } + + pub fn add_factor_source_to_confirmation_override( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.add_factor_source_to_confirmation_override( + factor_source_id.clone().into(), + ) + }) + } +} + +impl SecurityShieldBuilder { + fn _validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.get(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + factor_source_kind + ) + }) + } + + fn _validation_for_addition_of_factor_source_of_kind_to_recovery_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.get(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_recovery_override( + factor_source_kind + ) + }) + } + + fn _validation_for_addition_of_factor_source_of_kind_to_primary_override( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.get(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_primary_override( + factor_source_kind + ) + }) + } + + fn _validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + &self, + factor_source_kind: FactorSourceKind, + ) -> RoleBuilderMutateResult { + self.get(|builder| { + builder.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + factor_source_kind + ) + }) + } +} + +impl SecurityShieldBuilder { + /// Returns `true` for `Ok` and `Err(NotYetValid)`. + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_primary_threshold(factor_source_kind).is_valid_or_can_be() + } + + /// Returns `true` for `Ok` and `Err(NotYetValid)`. + pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_primary_override(factor_source_kind).is_valid_or_can_be() + } +} + +pub trait IsValidOrCanBecomeValid { + fn is_valid_or_can_be(&self) -> bool; +} +impl IsValidOrCanBecomeValid for Result { + fn is_valid_or_can_be(&self) -> bool { + match self { + Ok(_) => true, + Err(RoleBuilderValidation::BasicViolation(_)) => false, + Err(RoleBuilderValidation::ForeverInvalid(_)) => false, + Err(RoleBuilderValidation::NotYetValid(_)) => true, + } + } +} + +impl SecurityShieldBuilder { + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_primary_threshold(factor_source_kind) + .is_ok() + } + + pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_primary_override(factor_source_kind) + .is_ok() + } +} + +impl SecurityShieldBuilder { + pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( + &self, + factor_sources: Vec, + ) -> Vec { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder + .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( + &self, + factor_sources: Vec, + ) -> Vec { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder.validation_for_addition_of_factor_source_to_primary_override_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( + &self, + factor_sources: Vec, + ) -> Vec { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder + .validation_for_addition_of_factor_source_to_recovery_override_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( + &self, + factor_sources: Vec, + ) -> Vec { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder.validation_for_addition_of_factor_source_to_confirmation_override_for_each( + input, + ) + }, + ) + } +} + +impl SecurityShieldBuilder { + /// `None` means valid! + pub fn validate(&self) -> Option { + self.get(|builder| { + let r = builder.validate(); + r.as_shield_validation() + }) + } + + pub fn build( + &self, + ) -> Result< + SecurityStructureOfFactorSourceIds, + SecurityShieldBuilderInvalidReason, + > { + let matrix_result = self.get(|builder| builder.build()); + + if let Some(validation_error) = matrix_result.as_shield_validation() { + return Err(validation_error); + }; + assert!( + matrix_result.is_ok(), + "Programmer error, bad implementation of `into_validation`" + ); + let matrix_of_factors = matrix_result.unwrap(); + + let name = self.get_name(); + let display_name = DisplayName::new(name).map_err(|e| { + SecurityShieldBuilderInvalidReason::ShieldNameInvalid + })?; + + let metadata = SecurityStructureMetadata::new(display_name); + + let shield = SecurityStructureOfFactorSourceIds { + matrix_of_factors, + metadata, + }; + Ok(shield) + } +} + +pub trait AsShieldBuilderViolation { + fn as_shield_validation( + &self, + ) -> Option; +} + +impl AsShieldBuilderViolation for Result { + fn as_shield_validation( + &self, + ) -> Option { + match self { + Result::Err(err) => err.as_shield_validation(), + Result::Ok(_) => None, + } + } +} +impl AsShieldBuilderViolation for MatrixBuilderValidation { + fn as_shield_validation( + &self, + ) -> Option { + Some(SecurityShieldBuilderInvalidReason::Unknown) + } +} + +impl AsShieldBuilderViolation for MatrixRolesInCombinationViolation { + fn as_shield_validation( + &self, + ) -> Option { + match self { + Self::Basic(val) => val.as_shield_validation(), + Self::ForeverInvalid(val) => val.as_shield_validation(), + Self::NotYetValid(val) => val.as_shield_validation(), + } + } +} + +impl AsShieldBuilderViolation for MatrixRolesInCombinationBasicViolation { + fn as_shield_validation( + &self, + ) -> Option { + Some(SecurityShieldBuilderInvalidReason::Unknown) + } +} +impl AsShieldBuilderViolation for MatrixRolesInCombinationForeverInvalid { + fn as_shield_validation( + &self, + ) -> Option { + Some(SecurityShieldBuilderInvalidReason::Unknown) + } +} +impl AsShieldBuilderViolation for MatrixRolesInCombinationNotYetValid { + fn as_shield_validation( + &self, + ) -> Option { + Some(SecurityShieldBuilderInvalidReason::Unknown) + } +} + +impl AsShieldBuilderViolation for (RoleKind, RoleBuilderValidation) { + fn as_shield_validation( + &self, + ) -> Option { + Some(SecurityShieldBuilderInvalidReason::Unknown) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SecurityShieldBuilderInvalidReason { + ShieldNameInvalid, + Unknown, +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = SecurityShieldBuilder; + + // #[test] + // fn test() { + // let sut = SUT::new(); + + // assert_eq!(sut.get_name(), "My Shield"); + // sut.set_name("S.H.I.E.L.D.".to_owned()); + + // assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); + // sut.set_number_of_days_until_auto_confirm(u16::MAX).unwrap(); + // assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); + + // // Primary + // let sim_prim = + // sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + // FactorSourceID::sample_arculus(), + // ]); + + // let sim_prim_threshold = sut + // .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + // FactorSourceID::sample_arculus(), + // ]); + + // let sim_kind_prim = sut + // .validation_for_addition_of_factor_source_of_kind_to_primary_override( + // FactorSourceKind::Device, + // ); + + // let sim_kind_prim_threshold = sut + // .validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + // FactorSourceKind::Device, + // ); + + // sut.add_factor_source_to_primary_threshold( + // FactorSourceID::sample_device(), + // ) + // .unwrap(); + // assert_eq!( + // sut.get_primary_threshold_factors(), + // vec![FactorSourceID::sample_device()] + // ); + // _ = sut.set_threshold(1); + // assert_eq!(sut.get_primary_threshold(), 1); + // sut.add_factor_source_to_primary_override( + // FactorSourceID::sample_arculus(), + // ) + // .unwrap(); + // sut.add_factor_source_to_primary_override( + // FactorSourceID::sample_arculus_other(), + // ) + // .unwrap(); + + // assert_eq!( + // sut.get_primary_override_factors(), + // vec![ + // FactorSourceID::sample_arculus(), + // FactorSourceID::sample_arculus_other() + // ] + // ); + + // // Recovery + // let sim_rec = + // sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + // FactorSourceID::sample_ledger(), + // ]); + + // let sim_kind_rec = sut + // .validation_for_addition_of_factor_source_of_kind_to_recovery_override( + // FactorSourceKind::ArculusCard, + // ); + + // sut.add_factor_source_to_recovery_override( + // FactorSourceID::sample_ledger(), + // ) + // .unwrap(); + // sut.add_factor_source_to_recovery_override( + // FactorSourceID::sample_ledger_other(), + // ) + // .unwrap(); + + // assert_eq!( + // sut.get_recovery_factors(), + // vec![ + // FactorSourceID::sample_ledger(), + // FactorSourceID::sample_ledger_other() + // ] + // ); + + // // Confirmation + // let sim_conf = sut + // .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + // FactorSourceID::sample_device(), + // ]); + + // let sim_kind_conf = sut + // .validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + // FactorSourceKind::ArculusCard, + // ); + + // sut.add_factor_source_to_confirmation_override( + // FactorSourceID::sample_device(), + // ) + // .unwrap(); + + // assert_eq!( + // sut.get_confirmation_factors(), + // vec![FactorSourceID::sample_device(),] + // ); + + // assert_ne!( + // sim_prim, + // sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + // FactorSourceID::sample_arculus(), + // ]) + // ); + + // assert_ne!( + // sim_prim_threshold, + // sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + // FactorSourceID::sample_arculus() + // ]) + // ); + + // assert_ne!( + // sim_rec, + // sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + // FactorSourceID::sample_ledger(), + // ]) + // ); + + // assert_ne!( + // sim_conf, + // sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + // FactorSourceID::sample_device(), + // ]) + // ); + + // assert_ne!( + // sim_kind_prim, + // sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + // FactorSourceKind::Device, + // ) + // ); + + // assert_ne!( + // sim_kind_prim_threshold, + // sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + // FactorSourceKind::Device, + // ) + // ); + + // assert_eq!( + // sim_kind_rec, + // sut.validation_for_addition_of_factor_source_of_kind_to_recovery_override( + // FactorSourceKind::ArculusCard, + // ) + // ); + + // assert_eq!( + // sim_kind_conf, + // sut.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + // FactorSourceKind::ArculusCard, + // ) + // ); + + // sut.remove_factor(FactorSourceID::sample_arculus_other()) + // .unwrap(); + // sut.remove_factor(FactorSourceID::sample_ledger_other()) + // .unwrap(); + + // let v0 = sut.validate(); + // let v1 = sut.validate(); // can call validate many times! + // assert_eq!(v0, v1); + + // let shield = sut.build().unwrap(); // can build only once! (but can build after `validate`) + // assert_eq!( + // shield.matrix_builder.metadata.display_name.value, + // "S.H.I.E.L.D." + // ); + // assert_eq!( + // shield + // .matrix_builder + // .matrix_of_factors + // .primary() + // .get_override_factors(), + // &vec![FactorSourceID::sample_arculus().into()] + // ); + // assert_eq!( + // shield + // .matrix_builder + // .matrix_of_factors + // .recovery() + // .get_override_factors(), + // &vec![FactorSourceID::sample_ledger().into()] + // ); + // assert_eq!( + // shield + // .matrix_builder + // .matrix_of_factors + // .confirmation() + // .get_override_factors(), + // &vec![FactorSourceID::sample_device().into()] + // ); + // } +} diff --git a/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift b/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift index f24a9e7bb..a05e36ffc 100644 --- a/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift +++ b/examples/iOS/Backend/Sources/Planbok/Dependencies/Keychain.swift @@ -2,7 +2,7 @@ import ComposableArchitecture @_exported import KeychainAccess import Sargon -// MARK: - Keychain + Sendable +// MARK: - Keychain + @unchecked Sendable extension Keychain: @unchecked Sendable {} // MARK: - Keychain + SecureStorageDriver diff --git a/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift b/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift index 14937ceb8..740232c95 100644 --- a/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift +++ b/examples/iOS/Backend/Sources/Planbok/Features/OverlayWindow/HUDFeature.swift @@ -133,7 +133,7 @@ extension View { } } -// MARK: - SwiftUI.Animation + Sendable +// MARK: - SwiftUI.Animation + @unchecked Sendable extension SwiftUI.Animation: @unchecked Sendable {} extension SwiftUI.Animation { diff --git a/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift b/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift index 4e9532c2e..a1af8cb8e 100644 --- a/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift +++ b/examples/iOS/Backend/Sources/Planbok/SharedState/SargonKey.swift @@ -2,10 +2,10 @@ import ComposableArchitecture import Foundation import Sargon -// MARK: - PersistenceKeyDefault + Sendable +// MARK: - PersistenceKeyDefault + @unchecked Sendable extension PersistenceKeyDefault: @unchecked Sendable where Base: Sendable {} -// MARK: - KeyPath + Sendable +// MARK: - KeyPath + @unchecked Sendable extension KeyPath: @unchecked Sendable where Root: Sendable, Value: Sendable {} // MARK: - SargonKey From 3d941def5818464ffb91bc6ff38035fea04ad852 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 16:40:04 +0100 Subject: [PATCH 25/70] [no ci] wip --- .../security_shield_builder.rs | 337 ++++++------------ 1 file changed, 106 insertions(+), 231 deletions(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 44d6304fb..9f262ef36 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -6,20 +6,23 @@ pub struct SecurityShieldBuilder { name: RwLock, } +impl SecurityShieldBuilder { + pub fn new() -> Self { + let matrix_builder = MatrixBuilder::new(); + let name = RwLock::new("My Shield".to_owned()); + Self { + matrix_builder: RwLock::new(matrix_builder), + name, + } + } +} + impl SecurityShieldBuilder { fn get(&self, access: impl Fn(&MatrixBuilder) -> R) -> R { let binding = self.matrix_builder.read().unwrap(); access(&binding) } - fn with>( - &self, - mut write: impl FnMut(&mut MatrixBuilder) -> Result, - ) -> Result { - let mut binding = self.matrix_builder.write().expect("No poison"); - write(&mut binding).map_err(|e| Into::::into(e)) - } - // Ignores error and returns a ref to self fn set(&self, mut write: impl FnMut(&mut MatrixBuilder) -> R) -> &Self { let mut binding = self.matrix_builder.write().expect("No poison"); @@ -36,17 +39,13 @@ impl SecurityShieldBuilder { ) -> IndexSet, ) -> Vec { - // let input = &factor_sources - // .clone() - // .into_iter() - // .map(Into::::into) - // .collect::>(); - // self.with(|builder| { - // let xs = call(builder, input); - // Ok::<_, CommonError>(xs) - // }) - // .expect("No poison") - todo!() + let input = factor_sources + .iter() + .map(|x| x.clone().into()) + .collect::>(); + self.get(|builder| call(builder, &input)) + .into_iter() + .collect_vec() } } @@ -102,8 +101,8 @@ impl SecurityShieldBuilder { // ===== MUTATION ===== // ==================== impl SecurityShieldBuilder { - pub fn set_name(&self, name: String) -> &Self { - *self.name.write().unwrap() = name; + pub fn set_name(&self, name: impl AsRef) -> &Self { + *self.name.write().unwrap() = name.as_ref().to_owned(); self } @@ -130,11 +129,8 @@ impl SecurityShieldBuilder { }) } - pub fn remove_factor( - &self, - factor_source_id: FactorSourceID, - ) -> Result<(), CommonError> { - self.with(|builder| { + pub fn remove_factor(&self, factor_source_id: FactorSourceID) -> &Self { + self.set(|builder| { builder.remove_factor(&factor_source_id.clone().into()) }) } @@ -237,6 +233,22 @@ impl SecurityShieldBuilder { ) -> bool { self._validation_for_addition_of_factor_source_of_kind_to_primary_override(factor_source_kind).is_valid_or_can_be() } + + /// Returns `true` for `Ok` and `Err(NotYetValid)`. + pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_recovery_override(factor_source_kind).is_valid_or_can_be() + } + + /// Returns `true` for `Ok` and `Err(NotYetValid)`. + pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_confirmation_override(factor_source_kind).is_valid_or_can_be() + } } pub trait IsValidOrCanBecomeValid { @@ -269,6 +281,22 @@ impl SecurityShieldBuilder { self._validation_for_addition_of_factor_source_of_kind_to_primary_override(factor_source_kind) .is_ok() } + + pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_recovery_override(factor_source_kind) + .is_ok() + } + + pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self._validation_for_addition_of_factor_source_of_kind_to_confirmation_override(factor_source_kind) + .is_ok() + } } impl SecurityShieldBuilder { @@ -279,8 +307,7 @@ impl SecurityShieldBuilder { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { - builder - .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) + builder.validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) }, ) } @@ -446,207 +473,55 @@ mod tests { #[allow(clippy::upper_case_acronyms)] type SUT = SecurityShieldBuilder; - // #[test] - // fn test() { - // let sut = SUT::new(); - - // assert_eq!(sut.get_name(), "My Shield"); - // sut.set_name("S.H.I.E.L.D.".to_owned()); - - // assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); - // sut.set_number_of_days_until_auto_confirm(u16::MAX).unwrap(); - // assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); - - // // Primary - // let sim_prim = - // sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - // FactorSourceID::sample_arculus(), - // ]); - - // let sim_prim_threshold = sut - // .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - // FactorSourceID::sample_arculus(), - // ]); - - // let sim_kind_prim = sut - // .validation_for_addition_of_factor_source_of_kind_to_primary_override( - // FactorSourceKind::Device, - // ); - - // let sim_kind_prim_threshold = sut - // .validation_for_addition_of_factor_source_of_kind_to_primary_threshold( - // FactorSourceKind::Device, - // ); - - // sut.add_factor_source_to_primary_threshold( - // FactorSourceID::sample_device(), - // ) - // .unwrap(); - // assert_eq!( - // sut.get_primary_threshold_factors(), - // vec![FactorSourceID::sample_device()] - // ); - // _ = sut.set_threshold(1); - // assert_eq!(sut.get_primary_threshold(), 1); - // sut.add_factor_source_to_primary_override( - // FactorSourceID::sample_arculus(), - // ) - // .unwrap(); - // sut.add_factor_source_to_primary_override( - // FactorSourceID::sample_arculus_other(), - // ) - // .unwrap(); - - // assert_eq!( - // sut.get_primary_override_factors(), - // vec![ - // FactorSourceID::sample_arculus(), - // FactorSourceID::sample_arculus_other() - // ] - // ); - - // // Recovery - // let sim_rec = - // sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - // FactorSourceID::sample_ledger(), - // ]); - - // let sim_kind_rec = sut - // .validation_for_addition_of_factor_source_of_kind_to_recovery_override( - // FactorSourceKind::ArculusCard, - // ); - - // sut.add_factor_source_to_recovery_override( - // FactorSourceID::sample_ledger(), - // ) - // .unwrap(); - // sut.add_factor_source_to_recovery_override( - // FactorSourceID::sample_ledger_other(), - // ) - // .unwrap(); - - // assert_eq!( - // sut.get_recovery_factors(), - // vec![ - // FactorSourceID::sample_ledger(), - // FactorSourceID::sample_ledger_other() - // ] - // ); - - // // Confirmation - // let sim_conf = sut - // .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - // FactorSourceID::sample_device(), - // ]); - - // let sim_kind_conf = sut - // .validation_for_addition_of_factor_source_of_kind_to_confirmation_override( - // FactorSourceKind::ArculusCard, - // ); - - // sut.add_factor_source_to_confirmation_override( - // FactorSourceID::sample_device(), - // ) - // .unwrap(); - - // assert_eq!( - // sut.get_confirmation_factors(), - // vec![FactorSourceID::sample_device(),] - // ); - - // assert_ne!( - // sim_prim, - // sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - // FactorSourceID::sample_arculus(), - // ]) - // ); - - // assert_ne!( - // sim_prim_threshold, - // sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - // FactorSourceID::sample_arculus() - // ]) - // ); - - // assert_ne!( - // sim_rec, - // sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - // FactorSourceID::sample_ledger(), - // ]) - // ); - - // assert_ne!( - // sim_conf, - // sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - // FactorSourceID::sample_device(), - // ]) - // ); - - // assert_ne!( - // sim_kind_prim, - // sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( - // FactorSourceKind::Device, - // ) - // ); - - // assert_ne!( - // sim_kind_prim_threshold, - // sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( - // FactorSourceKind::Device, - // ) - // ); - - // assert_eq!( - // sim_kind_rec, - // sut.validation_for_addition_of_factor_source_of_kind_to_recovery_override( - // FactorSourceKind::ArculusCard, - // ) - // ); - - // assert_eq!( - // sim_kind_conf, - // sut.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( - // FactorSourceKind::ArculusCard, - // ) - // ); - - // sut.remove_factor(FactorSourceID::sample_arculus_other()) - // .unwrap(); - // sut.remove_factor(FactorSourceID::sample_ledger_other()) - // .unwrap(); - - // let v0 = sut.validate(); - // let v1 = sut.validate(); // can call validate many times! - // assert_eq!(v0, v1); - - // let shield = sut.build().unwrap(); // can build only once! (but can build after `validate`) - // assert_eq!( - // shield.matrix_builder.metadata.display_name.value, - // "S.H.I.E.L.D." - // ); - // assert_eq!( - // shield - // .matrix_builder - // .matrix_of_factors - // .primary() - // .get_override_factors(), - // &vec![FactorSourceID::sample_arculus().into()] - // ); - // assert_eq!( - // shield - // .matrix_builder - // .matrix_of_factors - // .recovery() - // .get_override_factors(), - // &vec![FactorSourceID::sample_ledger().into()] - // ); - // assert_eq!( - // shield - // .matrix_builder - // .matrix_of_factors - // .confirmation() - // .get_override_factors(), - // &vec![FactorSourceID::sample_device().into()] - // ); - // } + #[test] + fn test() { + let sut = SUT::new(); + + let _ = sut + .set_name("S.H.I.E.L.D.") + // Primary + .set_number_of_days_until_auto_confirm(42) + .add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ) + .set_threshold(1) + .add_factor_source_to_primary_override( + FactorSourceID::sample_arculus(), + ) + .add_factor_source_to_primary_override( + FactorSourceID::sample_arculus_other(), + ) + // Recovery + .add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ) + .add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ) + // Confirmation + .add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ) + .remove_factor(FactorSourceID::sample_arculus_other()) + .remove_factor(FactorSourceID::sample_ledger_other()); + + let shield = sut.build().unwrap(); + + assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); + assert_eq!( + shield.matrix_of_factors.primary().get_override_factors(), + &vec![FactorSourceID::sample_arculus().into()] + ); + assert_eq!( + shield.matrix_of_factors.recovery().get_override_factors(), + &vec![FactorSourceID::sample_ledger().into()] + ); + assert_eq!( + shield + .matrix_of_factors + .confirmation() + .get_override_factors(), + &vec![FactorSourceID::sample_device().into()] + ); + } } From 1b38550e1be503a411de9c80fa3c5927d05ac4e6 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 2 Dec 2024 20:05:55 +0100 Subject: [PATCH 26/70] [no ci] WIP --- ... => SecurityShieldBuilder+Swifified.swift} | 0 .../security_shield_builder.rs | 195 +++++++++++++++++- 2 files changed, 193 insertions(+), 2 deletions(-) rename apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/{SecurityShieldBuilder+Swiftiefied.swift => SecurityShieldBuilder+Swifified.swift} (100%) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift similarity index 100% rename from apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swiftiefied.swift rename to apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 9f262ef36..0e6ab4ea8 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -258,8 +258,7 @@ impl IsValidOrCanBecomeValid for Result { fn is_valid_or_can_be(&self) -> bool { match self { Ok(_) => true, - Err(RoleBuilderValidation::BasicViolation(_)) => false, - Err(RoleBuilderValidation::ForeverInvalid(_)) => false, + Err(RoleBuilderValidation::BasicViolation(_)) | Err(RoleBuilderValidation::ForeverInvalid(_)) => false, Err(RoleBuilderValidation::NotYetValid(_)) => true, } } @@ -380,6 +379,7 @@ impl SecurityShieldBuilder { let name = self.get_name(); let display_name = DisplayName::new(name).map_err(|e| { + error!("Invalid DisplayName {:?}", e); SecurityShieldBuilderInvalidReason::ShieldNameInvalid })?; @@ -524,4 +524,195 @@ mod tests { &vec![FactorSourceID::sample_device().into()] ); } + + fn test_addition_of_factor_source_of_kind_to_primary( + list_kind: FactorListKind, + is_fully_valid: impl Fn(&SUT, FactorSourceKind) -> bool, + can_be: impl Fn(&SUT, FactorSourceKind) -> bool, + add: impl Fn(&SUT, FactorSourceID) -> &SUT, + ) { + let sut_owned = SUT::new(); + let sut = &sut_owned; + assert!(can_be(sut, FactorSourceKind::Device)); + + if list_kind == FactorListKind::Threshold { + assert!(!is_fully_valid(sut, FactorSourceKind::Password)); // never alone + assert!(can_be(sut, FactorSourceKind::Password)); // lenient + + // now lets adding a Device => subsequent calls to `is_fully_valid` will return false + add(sut, FactorSourceID::sample_device()); + add(sut, FactorSourceID::sample_ledger()); + + sut.set_threshold(2); + assert!(is_fully_valid(sut, FactorSourceKind::Password)); // not alone any more! + assert!(can_be(sut, FactorSourceKind::Password)); + } else { + // now lets adding a Device => subsequent calls to `is_fully_valid` will return false + add(sut, FactorSourceID::sample_device()); + } + + assert!(!is_fully_valid(sut, FactorSourceKind::Device)); + + // TODO: Unsure about this, we do not count current state and query "can I add (another) Device?" as something + // which can become valid. It would require deletion of current Device factor. Maybe we should change this? + // Not sure... lets keep it as is for now! And lets see how UI integration "feels". + assert!(!can_be(sut, FactorSourceKind::Device)); + + // make it valid again + sut.remove_factor(FactorSourceID::sample_device()); + + assert!(is_fully_valid(sut, FactorSourceKind::Device)); + assert!(can_be(sut, FactorSourceKind::Device)); + } + + #[test] + fn test_addition_of_factor_source_of_kind_to_primary_threshold() { + test_addition_of_factor_source_of_kind_to_primary( + FactorListKind::Threshold, + SUT::addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid, + SUT::addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be, + SUT::add_factor_source_to_primary_threshold, + ); + } + + #[test] + fn test_addition_of_factor_source_of_kind_to_primary_override() { + test_addition_of_factor_source_of_kind_to_primary( + FactorListKind::Override, + SUT::addition_of_factor_source_of_kind_to_primary_override_is_fully_valid, + SUT::addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be, + SUT::add_factor_source_to_primary_override, + ); + } + + #[test] + fn test_addition_of_factor_source_of_kind_to_recovery_is_fully_valid() { + let sut = SUT::new(); + + let result = sut + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::Device, + ); + assert!(result); + + let result = sut + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::Password, + ); + assert!(!result); + } + + #[test] + fn test_addition_of_factor_source_of_kind_to_confirmation_is_fully_valid() { + let sut = SUT::new(); + + let result = sut + .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::Device, + ); + assert!(result); + + let result = sut + .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::TrustedContact, + ); + assert!(!result); + } + + #[test] + fn test_validation_for_addition_of_factor_source_to_primary_threshold_for_each( + ) { + let sut = SUT::new(); + + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ); + + let xs = sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each( + vec![ + FactorSourceID::sample_device(), + FactorSourceID::sample_device_other(), + ], + ); + + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + vec![ + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Primary, + FactorSourceID::sample_device(), + ForeverInvalidReason::FactorSourceAlreadyPresent + ), + FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Primary, + FactorSourceID::sample_device_other(), + ForeverInvalidReason::PrimaryCannotHaveMultipleDevices + ), + ] + ); + } + + #[test] + fn test_validation_for_addition_of_factor_source_to_recovery_override_for_each() { + let sut = SUT::new(); + + let xs = sut.validation_for_addition_of_factor_source_to_recovery_override_for_each( + vec![ + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_security_questions(), + FactorSourceID::sample_security_questions_other(), + ], + ); + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + [ + FactorSourceID::sample_password(), + FactorSourceID::sample_password_other(), + FactorSourceID::sample_security_questions(), + FactorSourceID::sample_security_questions_other(), + ] + .into_iter() + .map( + |fsid| FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Recovery, + fsid, + if fsid.get_factor_source_kind() == FactorSourceKind::SecurityQuestions { + ForeverInvalidReason::RecoveryRoleSecurityQuestionsNotSupported + } else { + ForeverInvalidReason::RecoveryRolePasswordNotSupported + } + ) + ) + .collect::>() + ); + } + + #[test] + fn test_validation_for_addition_of_factor_source_to_confirmation_override_for_each() { + let sut = SUT::new(); + let xs = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each( + vec![ + FactorSourceID::sample_trusted_contact(), + FactorSourceID::sample_trusted_contact_other(), + ], + ); + pretty_assertions::assert_eq!( + xs.into_iter().collect::>(), + [ + FactorSourceID::sample_trusted_contact(), + FactorSourceID::sample_trusted_contact_other(), + ] + .into_iter() + .map( + |fsid| FactorSourceInRoleBuilderValidationStatus::forever_invalid( + RoleKind::Confirmation, + fsid, + ForeverInvalidReason::ConfirmationRoleTrustedContactNotSupported + ) + ) + .collect::>() + ); + } } From 2f512f97a6a93f4337b079fcf717b373c4e045a2 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 08:55:51 +0100 Subject: [PATCH 27/70] [no ci] WIP --- .../security_shield_builder.rs | 255 ++++++++++-------- .../security_shield_builder.rs | 6 +- 2 files changed, 153 insertions(+), 108 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index c165d0962..c6193851c 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -11,72 +11,64 @@ use sargon::{IndexSet, MatrixBuilder}; use crate::prelude::*; +/// A builder of `SecurityStructureOfFactorSourceIds` a.k.a. `SecurityShield`, +/// which contains a MatrixOfFactorSourceIds - with primary, recovery, and +/// confirmation roles. #[derive(Debug, uniffi::Object)] pub struct SecurityShieldBuilder { - wrapped: RwLock, + wrapped: RwLock, } -#[derive(Debug, PartialEq, Eq, Hash, uniffi::Object)] -#[uniffi::export(Debug, Eq, Hash)] -pub struct SecurityStructureOfFactorSourceIds { - pub wrapped: sargon::SecurityStructureOfFactorSourceIds, +#[uniffi::export] +impl SecurityShieldBuilder { + #[uniffi::constructor] + pub fn new() -> Arc { + Arc::new(Self { + wrapped: RwLock::new(sargon::SecurityShieldBuilder::new()), + }) + } } impl SecurityShieldBuilder { - fn get(&self, access: impl Fn(&MatrixBuilder) -> R) -> R { + fn get( + &self, + access: impl Fn(&sargon::SecurityShieldBuilder) -> R, + ) -> R { let binding = self.wrapped.read().unwrap(); access(&binding) } - fn with>( + fn set( &self, - mut write: impl FnMut(&mut MatrixBuilder) -> Result, - ) -> Result { + mut write: impl FnMut( + &mut sargon::SecurityShieldBuilder, + ) -> &sargon::SecurityShieldBuilder, + ) { let mut binding = self.wrapped.write().expect("No poison"); - write(&mut binding).map_err(|e| Into::::into(e)) - } - - fn set(&self, mut write: impl FnMut(&mut MatrixBuilder) -> R) -> R { - let mut binding = self.wrapped.write().expect("No poison"); - write(&mut binding) + _ = write(&mut binding); } fn validation_for_addition_of_factor_source_by_calling( &self, factor_sources: Vec, call: impl Fn( - &MatrixBuilder, - &IndexSet, + &sargon::SecurityShieldBuilder, + Vec, ) - -> IndexSet, + -> Vec, ) -> Vec> { - let input = &factor_sources + let input = factor_sources .clone() .into_iter() .map(Into::::into) - .collect::>(); - self.with(|builder| { - let xs = call(builder, input); + .collect_vec(); - let xs = xs + self.get(|builder| { + call(builder, input.clone()) .into_iter() .map(Into::::into) .map(Arc::new) - .collect(); - - Ok::<_, CommonError>(xs) - }) - .expect("Internal error - have you already called `build` on this builder? You should not continue using the builder after it has been built.") - } -} - -#[uniffi::export] -impl SecurityShieldBuilder { - #[uniffi::constructor] - pub fn new() -> Arc { - Arc::new(Self { - wrapped: RwLock::new(MatrixBuilder::new()), - name: RwLock::new("My Shield".to_owned()), + .collect() }) } } @@ -84,13 +76,15 @@ impl SecurityShieldBuilder { impl SecurityShieldBuilder { fn get_factors( &self, - access: impl Fn(&MatrixBuilder) -> &Vec, + access: impl Fn( + &sargon::SecurityShieldBuilder, + ) -> Vec, ) -> Vec { self.get(|builder| { let factors = access(builder); factors - .iter() - .map(|x| crate::FactorSourceID::from(*x)) + .into_iter() + .map(crate::FactorSourceID::from) .collect::>() }) } @@ -110,7 +104,7 @@ impl SecurityShieldBuilder { } pub fn get_name(&self) -> String { - self.name.read().unwrap().clone() + self.get(|builder| builder.get_name()) } pub fn get_primary_threshold_factors(&self) -> Vec { @@ -136,15 +130,15 @@ impl SecurityShieldBuilder { #[uniffi::export] impl SecurityShieldBuilder { pub fn set_name(&self, name: String) { - *self.name.write().unwrap() = name + self.set(|builder| builder.set_name(&name)); } /// Adds the factor source to the primary role threshold list. pub fn add_factor_source_to_primary_threshold( &self, factor_source_id: FactorSourceID, - ) -> Result<(), CommonError> { - self.with(|builder| { + ) { + self.set(|builder| { builder.add_factor_source_to_primary_threshold( factor_source_id.clone().into(), ) @@ -154,32 +148,26 @@ impl SecurityShieldBuilder { pub fn add_factor_source_to_primary_override( &self, factor_source_id: FactorSourceID, - ) -> Result<(), CommonError> { - self.with(|builder| { + ) { + self.set(|builder| { builder.add_factor_source_to_primary_override( factor_source_id.clone().into(), ) }) } - pub fn remove_factor( - &self, - factor_source_id: FactorSourceID, - ) -> Result<(), CommonError> { - self.with(|builder| { - builder.remove_factor(&factor_source_id.clone().into()) + pub fn remove_factor(&self, factor_source_id: FactorSourceID) { + self.set(|builder| { + builder.remove_factor(factor_source_id.clone().into()) }) } - pub fn set_threshold(&self, threshold: u8) -> Result<(), CommonError> { - self.with(|builder| builder.set_threshold(threshold)) + pub fn set_threshold(&self, threshold: u8) { + self.set(|builder| builder.set_threshold(threshold)) } - pub fn set_number_of_days_until_auto_confirm( - &self, - number_of_days: u16, - ) -> Result<(), CommonError> { - self.with(|builder| { + pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { + self.set(|builder| { builder.set_number_of_days_until_auto_confirm(number_of_days) }) } @@ -187,8 +175,8 @@ impl SecurityShieldBuilder { pub fn add_factor_source_to_recovery_override( &self, factor_source_id: FactorSourceID, - ) -> Result<(), CommonError> { - self.with(|builder| { + ) { + self.set(|builder| { builder.add_factor_source_to_recovery_override( factor_source_id.clone().into(), ) @@ -198,62 +186,109 @@ impl SecurityShieldBuilder { pub fn add_factor_source_to_confirmation_override( &self, factor_source_id: FactorSourceID, - ) -> Result<(), CommonError> { - self.with(|builder| { + ) { + self.set(|builder| { builder.add_factor_source_to_confirmation_override( factor_source_id.clone().into(), ) }) } +} - pub fn validation_for_addition_of_factor_source_of_kind_to_confirmation_override( +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( &self, factor_source_kind: FactorSourceKind, - ) -> Result<(), CommonError> { - self.with(|builder| { - builder.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( factor_source_kind.clone().into(), ) - .map_err(|e| Into::::into((RoleKind::Confirmation, e))) - }) + ) } - pub fn validation_for_addition_of_factor_source_of_kind_to_recovery_override( + pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( &self, factor_source_kind: FactorSourceKind, - ) -> Result<(), CommonError> { - self.with(|builder| { - builder.validation_for_addition_of_factor_source_of_kind_to_recovery_override( + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( factor_source_kind.clone().into(), ) - .map_err(|e| Into::::into((RoleKind::Recovery, e))) - }) + ) } - pub fn validation_for_addition_of_factor_source_of_kind_to_primary_override( + pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( &self, factor_source_kind: FactorSourceKind, - ) -> Result<(), CommonError> { - self.with(|builder| { - builder.validation_for_addition_of_factor_source_of_kind_to_primary_override( + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( factor_source_kind.clone().into(), ) - .map_err(|e| Into::::into((RoleKind::Primary, e))) + ) + } + + pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| { + builder + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + factor_source_kind.clone().into(), + ) }) } - pub fn validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( &self, factor_source_kind: FactorSourceKind, - ) -> Result<(), CommonError> { - self.with(|builder| { - builder.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( factor_source_kind.clone().into(), ) - .map_err(|e| Into::::into((RoleKind::Primary, e))) - }) + ) } + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) + } +} + +#[uniffi::export] +impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( &self, factor_sources: Vec, @@ -305,28 +340,35 @@ impl SecurityShieldBuilder { }, ) } +} + +use sargon::SecurityShieldBuilderInvalidReason as InternalSecurityShieldBuilderInvalidReason; - pub fn validate(&self) -> Result<(), CommonError> { - self.with(|builder| builder.validate()) +#[derive(Clone, Copy, PartialEq, Eq, InternalConversion, uniffi::Enum)] +pub enum SecurityShieldBuilderInvalidReason { + ShieldNameInvalid, + Unknown, +} +delegate_display_debug_into!( + SecurityShieldBuilderInvalidReason, + InternalSecurityShieldBuilderInvalidReason +); + +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn validate(&self) -> Option { + self.get(|builder| builder.validate().map(|x| x.into())) } pub fn build( &self, - ) -> Result { - let wrapped_matrix = self.with(|builder| builder.build())?; - - let name = self.get_name(); - let display_name = - sargon::DisplayName::new(name).map_err(|e| CommonError::Unknown)?; // TODO CommonError display.. - let wrapped_shield = sargon::SecurityStructureOfFactorSourceIds::new( - display_name, - wrapped_matrix, - ); - - let shield = SecurityStructureOfFactorSourceIds { - wrapped: wrapped_shield, - }; - Ok(shield) + ) -> Result< + SecurityStructureOfFactorSourceIDs, + SecurityShieldBuilderInvalidReason, + > { + self.get(|builder| builder.build()) + .map(|shield| shield.into()) + .map_err(|x| x.into()) } } @@ -379,9 +421,9 @@ mod tests { sut.set_name("S.H.I.E.L.D.".to_owned()); assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); - sut.set_number_of_days_until_auto_confirm(u16::MAX).unwrap(); + sut.set_number_of_days_until_auto_confirm(u16::MAX); assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); - + /* // Primary let sim_prim = sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ @@ -570,5 +612,6 @@ mod tests { .get_override_factors(), &vec![FactorSourceID::sample_device().into()] ); + */ } } diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 0e6ab4ea8..d63e44fb7 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -68,7 +68,7 @@ impl SecurityShieldBuilder { // ==== GET / READ ==== // ==================== impl SecurityShieldBuilder { - pub fn get_primary_threshold(&self) -> u8 { + pub fn get_threshold(&self) -> u8 { self.get(|builder| builder.get_threshold()) } @@ -459,9 +459,11 @@ impl AsShieldBuilderViolation for (RoleKind, RoleBuilderValidation) { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] pub enum SecurityShieldBuilderInvalidReason { + #[error("Shield name is invalid")] ShieldNameInvalid, + #[error("Shield unknown error")] Unknown, } From d2a1b5030d6692e3409c3ebced8c8703ef8124fb Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 09:43:44 +0100 Subject: [PATCH 28/70] [no ci] wip --- .../security_shield_builder.rs | 155 ++++++++---------- .../security_structure_metadata.rs | 2 + .../security_structure_of_factor_instances.rs | 3 + 3 files changed, 73 insertions(+), 87 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index c6193851c..49907bb43 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -133,6 +133,22 @@ impl SecurityShieldBuilder { self.set(|builder| builder.set_name(&name)); } + pub fn remove_factor(&self, factor_source_id: FactorSourceID) { + self.set(|builder| { + builder.remove_factor(factor_source_id.clone().into()) + }) + } + + pub fn set_threshold(&self, threshold: u8) { + self.set(|builder| builder.set_threshold(threshold)) + } + + pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { + self.set(|builder| { + builder.set_number_of_days_until_auto_confirm(number_of_days) + }) + } + /// Adds the factor source to the primary role threshold list. pub fn add_factor_source_to_primary_threshold( &self, @@ -156,22 +172,6 @@ impl SecurityShieldBuilder { }) } - pub fn remove_factor(&self, factor_source_id: FactorSourceID) { - self.set(|builder| { - builder.remove_factor(factor_source_id.clone().into()) - }) - } - - pub fn set_threshold(&self, threshold: u8) { - self.set(|builder| builder.set_threshold(threshold)) - } - - pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { - self.set(|builder| { - builder.set_number_of_days_until_auto_confirm(number_of_days) - }) - } - pub fn add_factor_source_to_recovery_override( &self, factor_source_id: FactorSourceID, @@ -197,90 +197,90 @@ impl SecurityShieldBuilder { #[uniffi::export] impl SecurityShieldBuilder { - pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| - builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( + builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( factor_source_kind.clone().into(), ) ) } - pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| - builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( factor_source_kind.clone().into(), ) ) } - pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( + pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| - builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( + builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( factor_source_kind.clone().into(), ) ) } - pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( &self, factor_source_kind: FactorSourceKind, ) -> bool { - self.get(|builder| { - builder - .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - factor_source_kind.clone().into(), - ) - }) + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) } - pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( factor_source_kind.clone().into(), ) ) } - pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( + pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( &self, factor_source_kind: FactorSourceKind, ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( - factor_source_kind.clone().into(), - ) - ) + self.get(|builder| { + builder + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + factor_source_kind.clone().into(), + ) + }) } - pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( factor_source_kind.clone().into(), ) ) } - pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( factor_source_kind.clone().into(), ) ) @@ -423,7 +423,6 @@ mod tests { assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); sut.set_number_of_days_until_auto_confirm(u16::MAX); assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); - /* // Primary let sim_prim = sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ @@ -436,19 +435,18 @@ mod tests { ]); let sim_kind_prim = sut - .validation_for_addition_of_factor_source_of_kind_to_primary_override( + .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( FactorSourceKind::Device, ); let sim_kind_prim_threshold = sut - .validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( FactorSourceKind::Device, ); sut.add_factor_source_to_primary_threshold( FactorSourceID::sample_device(), - ) - .unwrap(); + ); assert_eq!( sut.get_primary_threshold_factors(), vec![FactorSourceID::sample_device()] @@ -457,12 +455,10 @@ mod tests { assert_eq!(sut.get_primary_threshold(), 1); sut.add_factor_source_to_primary_override( FactorSourceID::sample_arculus(), - ) - .unwrap(); + ); sut.add_factor_source_to_primary_override( FactorSourceID::sample_arculus_other(), - ) - .unwrap(); + ); assert_eq!( sut.get_primary_override_factors(), @@ -479,18 +475,16 @@ mod tests { ]); let sim_kind_rec = sut - .validation_for_addition_of_factor_source_of_kind_to_recovery_override( + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( FactorSourceKind::ArculusCard, ); sut.add_factor_source_to_recovery_override( FactorSourceID::sample_ledger(), - ) - .unwrap(); + ); sut.add_factor_source_to_recovery_override( FactorSourceID::sample_ledger_other(), - ) - .unwrap(); + ); assert_eq!( sut.get_recovery_factors(), @@ -507,14 +501,13 @@ mod tests { ]); let sim_kind_conf = sut - .validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( FactorSourceKind::ArculusCard, ); sut.add_factor_source_to_confirmation_override( FactorSourceID::sample_device(), - ) - .unwrap(); + ); assert_eq!( sut.get_confirmation_factors(), @@ -551,67 +544,55 @@ mod tests { assert_ne!( sim_kind_prim, - sut.validation_for_addition_of_factor_source_of_kind_to_primary_override( + sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( FactorSourceKind::Device, ) ); assert_ne!( sim_kind_prim_threshold, - sut.validation_for_addition_of_factor_source_of_kind_to_primary_threshold( + sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( FactorSourceKind::Device, ) ); assert_eq!( sim_kind_rec, - sut.validation_for_addition_of_factor_source_of_kind_to_recovery_override( + sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( FactorSourceKind::ArculusCard, ) ); assert_eq!( sim_kind_conf, - sut.validation_for_addition_of_factor_source_of_kind_to_confirmation_override( + sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( FactorSourceKind::ArculusCard, ) ); - sut.remove_factor(FactorSourceID::sample_arculus_other()) - .unwrap(); - sut.remove_factor(FactorSourceID::sample_ledger_other()) - .unwrap(); + sut.remove_factor(FactorSourceID::sample_arculus_other()); + sut.remove_factor(FactorSourceID::sample_ledger_other()); let v0 = sut.validate(); let v1 = sut.validate(); // can call validate many times! assert_eq!(v0, v1); - let shield = sut.build().unwrap(); // can build only once! (but can build after `validate`) - assert_eq!(shield.wrapped.metadata.display_name.value, "S.H.I.E.L.D."); + let shield0 = sut.build().unwrap(); + let shield = sut.build().unwrap(); // can call build many times! + assert_eq!(shield0, shield); + + assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); assert_eq!( - shield - .wrapped - .matrix_of_factors - .primary() - .get_override_factors(), - &vec![FactorSourceID::sample_arculus().into()] + shield.matrix_of_factors.primary_role.override_factors, + vec![FactorSourceID::sample_arculus().into()] ); assert_eq!( - shield - .wrapped - .matrix_of_factors - .recovery() - .get_override_factors(), - &vec![FactorSourceID::sample_ledger().into()] + shield.matrix_of_factors.recovery_role.override_factors, + vec![FactorSourceID::sample_ledger().into()] ); assert_eq!( - shield - .wrapped - .matrix_of_factors - .confirmation() - .get_override_factors(), - &vec![FactorSourceID::sample_device().into()] + shield.matrix_of_factors.confirmation_role.override_factors, + vec![FactorSourceID::sample_device().into()] ); - */ } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs index 38f2ede0d..e45cdedae 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs @@ -9,6 +9,8 @@ pub struct SecurityStructureMetadata { pub last_updated_on: Timestamp, } +delegate_display_debug_into!(SecurityStructureMetadata, InternalSecurityStructureMetadata); + #[uniffi::export] pub fn new_security_structure_metadata_sample() -> SecurityStructureMetadata { InternalSecurityStructureMetadata::sample().into() diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs index 7889cb04c..7fc08a9ab 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs @@ -27,4 +27,7 @@ pub struct SecurityStructureOfFactorSourceIDs { pub matrix_of_factors: MatrixOfFactorSourceIDs, } + +delegate_display_debug_into!(SecurityStructureOfFactorSourceIDs, InternalSecurityStructureOfFactorSourceIDs); + pub type MatrixOfFactorSourceIds = MatrixOfFactorSourceIDs; From 81d8d931a4c9c5848a90621caaeac79e4de7f42f Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 10:30:02 +0100 Subject: [PATCH 29/70] [no ci] wip --- .../security_structure_metadata.rs | 5 +++- .../security_structure_of_factor_instances.rs | 6 +++-- .../security_shield_builder.rs | 26 ++++++++++++++++--- ...security_structure_of_factor_source_ids.rs | 2 +- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs index e45cdedae..58e5517b6 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structure_metadata.rs @@ -9,7 +9,10 @@ pub struct SecurityStructureMetadata { pub last_updated_on: Timestamp, } -delegate_display_debug_into!(SecurityStructureMetadata, InternalSecurityStructureMetadata); +delegate_debug_into!( + SecurityStructureMetadata, + InternalSecurityStructureMetadata +); #[uniffi::export] pub fn new_security_structure_metadata_sample() -> SecurityStructureMetadata { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs index 7fc08a9ab..ae7ccf3d8 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs @@ -27,7 +27,9 @@ pub struct SecurityStructureOfFactorSourceIDs { pub matrix_of_factors: MatrixOfFactorSourceIDs, } - -delegate_display_debug_into!(SecurityStructureOfFactorSourceIDs, InternalSecurityStructureOfFactorSourceIDs); +delegate_debug_into!( + SecurityStructureOfFactorSourceIDs, + InternalSecurityStructureOfFactorSourceIDs +); pub type MatrixOfFactorSourceIds = MatrixOfFactorSourceIDs; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index d63e44fb7..889fbd1f2 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -4,6 +4,12 @@ use crate::prelude::*; pub struct SecurityShieldBuilder { matrix_builder: RwLock, name: RwLock, + // We eagerly set this, and we use it inside the `build` method, ensuring + // that for the same *state* of `MatrixBuilder` we always have the same shield! + shield_id: SecurityStructureID, + // We eagerly set this, and we use it inside the `build` method, ensuring + // that for the same *state* of `MatrixBuilder` we always have the same shield! + created_on: Timestamp, } impl SecurityShieldBuilder { @@ -13,6 +19,8 @@ impl SecurityShieldBuilder { Self { matrix_builder: RwLock::new(matrix_builder), name, + shield_id: SecurityStructureID::from(id()), + created_on: now(), } } } @@ -258,7 +266,8 @@ impl IsValidOrCanBecomeValid for Result { fn is_valid_or_can_be(&self) -> bool { match self { Ok(_) => true, - Err(RoleBuilderValidation::BasicViolation(_)) | Err(RoleBuilderValidation::ForeverInvalid(_)) => false, + Err(RoleBuilderValidation::BasicViolation(_)) + | Err(RoleBuilderValidation::ForeverInvalid(_)) => false, Err(RoleBuilderValidation::NotYetValid(_)) => true, } } @@ -383,7 +392,12 @@ impl SecurityShieldBuilder { SecurityShieldBuilderInvalidReason::ShieldNameInvalid })?; - let metadata = SecurityStructureMetadata::new(display_name); + let metadata = SecurityStructureMetadata::with_details( + self.shield_id, + display_name, + self.created_on, + self.created_on, + ); let shield = SecurityStructureOfFactorSourceIds { matrix_of_factors, @@ -507,7 +521,9 @@ mod tests { .remove_factor(FactorSourceID::sample_arculus_other()) .remove_factor(FactorSourceID::sample_ledger_other()); + let shield0 = sut.build().unwrap(); let shield = sut.build().unwrap(); + pretty_assertions::assert_eq!(shield0, shield); assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); assert_eq!( @@ -655,7 +671,8 @@ mod tests { } #[test] - fn test_validation_for_addition_of_factor_source_to_recovery_override_for_each() { + fn test_validation_for_addition_of_factor_source_to_recovery_override_for_each( + ) { let sut = SUT::new(); let xs = sut.validation_for_addition_of_factor_source_to_recovery_override_for_each( @@ -691,7 +708,8 @@ mod tests { } #[test] - fn test_validation_for_addition_of_factor_source_to_confirmation_override_for_each() { + fn test_validation_for_addition_of_factor_source_to_confirmation_override_for_each( + ) { let sut = SUT::new(); let xs = sut .validation_for_addition_of_factor_source_to_confirmation_override_for_each( diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs index d66b89f9c..0ae91a796 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_source_ids.rs @@ -3,7 +3,7 @@ use crate::prelude::*; pub type SecurityStructureOfFactorSourceIds = AbstractSecurityStructure; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] #[serde(rename_all = "camelCase")] pub struct SecurityStructureOfFactorInstances { /// The ID of the `SecurityStructureOfFactorSourceIDs` in From 60182e52255ef129f7438c9ddf1f3ad76571c3b3 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 10:48:45 +0100 Subject: [PATCH 30/70] [no ci] wip --- .../profile/mfa/security_structures/mod.rs | 4 +- .../security_shield_builder.rs | 12 - .../security_shield_builder_invalid_reason.rs | 99 +++++++ .../profile/mfa/security_structures/mod.rs | 2 + .../security_shield_builder.rs | 74 ----- .../security_shield_builder_invalid_reason.rs | 272 ++++++++++++++++++ 6 files changed, 376 insertions(+), 87 deletions(-) create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index 1f32904c3..be7377e4c 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,8 +1,9 @@ -mod security_shield_builder; mod error_conversion; mod matrices; mod models; mod roles; +mod security_shield_builder; +mod security_shield_builder_invalid_reason; mod security_structure_id; mod security_structure_metadata; mod security_structures; @@ -10,6 +11,7 @@ mod security_structures; pub use matrices::*; pub use models::*; pub use roles::*; +pub use security_shield_builder_invalid_reason::*; pub use security_structure_id::*; pub use security_structure_metadata::*; pub use security_structures::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 49907bb43..215eba14d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -342,18 +342,6 @@ impl SecurityShieldBuilder { } } -use sargon::SecurityShieldBuilderInvalidReason as InternalSecurityShieldBuilderInvalidReason; - -#[derive(Clone, Copy, PartialEq, Eq, InternalConversion, uniffi::Enum)] -pub enum SecurityShieldBuilderInvalidReason { - ShieldNameInvalid, - Unknown, -} -delegate_display_debug_into!( - SecurityShieldBuilderInvalidReason, - InternalSecurityShieldBuilderInvalidReason -); - #[uniffi::export] impl SecurityShieldBuilder { pub fn validate(&self) -> Option { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs new file mode 100644 index 000000000..519a4f88b --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -0,0 +1,99 @@ +use crate::prelude::*; +use sargon::SecurityShieldBuilderInvalidReason as InternalSecurityShieldBuilderInvalidReason; + +use thiserror::Error as ThisError; + +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + InternalConversion, + ThisError, + uniffi::Error, +)] +pub enum SecurityShieldBuilderInvalidReason { + #[error("Shield name is invalid")] + ShieldNameInvalid, + + #[error("The factor source was not found in any role")] + FactorSourceNotFoundInAnyRole, + + #[error("The number of days until auto confirm must be greater than zero")] + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero, + + #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] + RecoveryAndConfirmationFactorsOverlap, + + #[error("The single factor used in the primary role must not be used in any other role")] + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, + + // ================== + // BasicViolation + // ================== + /// e.g. tried to remove a factor source which was not found. + #[error("FactorSourceID not found")] + FactorSourceNotFound, + + #[error("Recovery cannot set threshold")] + RecoveryCannotSetThreshold, + + #[error("Confirmation cannot set threshold")] + ConfirmationCannotSetThreshold, + + // ========================= + // NotYetValidReason + // ========================= + #[error("Role must have at least one factor")] + RoleMustHaveAtLeastOneFactor, + + #[error( + "Primary role with password in threshold list must have another factor" + )] + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + + #[error( + "Primary role with threshold factors cannot have a threshold of zero" + )] + PrimaryRoleWithThresholdCannotBeZeroWithFactors, + + #[error("Primary role with password in threshold list must have threshold greater than one")] + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + + #[error("Threshold higher than threshold factors len")] + ThresholdHigherThanThresholdFactorsLen, + + // ================================ + // ForeverInvalidReason + // ================================ + #[error("Factor source already present")] + FactorSourceAlreadyPresent, + + #[error("Primary role cannot have multiple devices")] + PrimaryCannotHaveMultipleDevices, + + #[error("Primary role cannot have password in override list")] + PrimaryCannotHavePasswordInOverrideList, + + #[error("Primary role cannot contain Security Questions")] + PrimaryCannotContainSecurityQuestions, + + #[error("Primary role cannot contain Trusted Contact")] + PrimaryCannotContainTrustedContact, + + #[error("Recovery role threshold list not supported")] + RecoveryRoleThresholdFactorsNotSupported, + + #[error("Recovery role Security Questions not supported")] + RecoveryRoleSecurityQuestionsNotSupported, + + #[error("Recovery role password not supported")] + RecoveryRolePasswordNotSupported, + + #[error("Confirmation role threshold list not supported")] + ConfirmationRoleThresholdFactorsNotSupported, + + #[error("Confirmation role cannot contain Trusted Contact")] + ConfirmationRoleTrustedContactNotSupported, +} diff --git a/crates/sargon/src/profile/mfa/security_structures/mod.rs b/crates/sargon/src/profile/mfa/security_structures/mod.rs index 3236bab04..fff1fff63 100644 --- a/crates/sargon/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/mod.rs @@ -5,9 +5,11 @@ mod security_shield_builder; mod security_structure_id; mod security_structure_metadata; mod security_structure_of_factors; +mod security_shield_builder_invalid_reason; pub use has_role_kind::*; pub use matrices::*; +pub use security_shield_builder_invalid_reason::*; pub use roles::*; pub use security_shield_builder::*; pub use security_structure_id::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 889fbd1f2..67de72527 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -407,80 +407,6 @@ impl SecurityShieldBuilder { } } -pub trait AsShieldBuilderViolation { - fn as_shield_validation( - &self, - ) -> Option; -} - -impl AsShieldBuilderViolation for Result { - fn as_shield_validation( - &self, - ) -> Option { - match self { - Result::Err(err) => err.as_shield_validation(), - Result::Ok(_) => None, - } - } -} -impl AsShieldBuilderViolation for MatrixBuilderValidation { - fn as_shield_validation( - &self, - ) -> Option { - Some(SecurityShieldBuilderInvalidReason::Unknown) - } -} - -impl AsShieldBuilderViolation for MatrixRolesInCombinationViolation { - fn as_shield_validation( - &self, - ) -> Option { - match self { - Self::Basic(val) => val.as_shield_validation(), - Self::ForeverInvalid(val) => val.as_shield_validation(), - Self::NotYetValid(val) => val.as_shield_validation(), - } - } -} - -impl AsShieldBuilderViolation for MatrixRolesInCombinationBasicViolation { - fn as_shield_validation( - &self, - ) -> Option { - Some(SecurityShieldBuilderInvalidReason::Unknown) - } -} -impl AsShieldBuilderViolation for MatrixRolesInCombinationForeverInvalid { - fn as_shield_validation( - &self, - ) -> Option { - Some(SecurityShieldBuilderInvalidReason::Unknown) - } -} -impl AsShieldBuilderViolation for MatrixRolesInCombinationNotYetValid { - fn as_shield_validation( - &self, - ) -> Option { - Some(SecurityShieldBuilderInvalidReason::Unknown) - } -} - -impl AsShieldBuilderViolation for (RoleKind, RoleBuilderValidation) { - fn as_shield_validation( - &self, - ) -> Option { - Some(SecurityShieldBuilderInvalidReason::Unknown) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] -pub enum SecurityShieldBuilderInvalidReason { - #[error("Shield name is invalid")] - ShieldNameInvalid, - #[error("Shield unknown error")] - Unknown, -} - #[cfg(test)] mod tests { diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs new file mode 100644 index 000000000..f8f2e382b --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -0,0 +1,272 @@ +use crate::prelude::*; + + +pub trait AsShieldBuilderViolation { + fn as_shield_validation( + &self, + ) -> Option; +} + +impl AsShieldBuilderViolation for Result { + fn as_shield_validation( + &self, + ) -> Option { + match self { + Result::Err(err) => err.as_shield_validation(), + Result::Ok(_) => None, + } + } +} +impl AsShieldBuilderViolation for MatrixBuilderValidation { + fn as_shield_validation( + &self, + ) -> Option { + match self { + MatrixBuilderValidation::RoleInIsolation { role, violation } => { + (*role, *violation).as_shield_validation() + } + MatrixBuilderValidation::CombinationViolation(violation) => { + violation.as_shield_validation() + } + } + } +} + +impl AsShieldBuilderViolation for MatrixRolesInCombinationViolation { + fn as_shield_validation( + &self, + ) -> Option { + match self { + Self::Basic(val) => val.as_shield_validation(), + Self::ForeverInvalid(val) => val.as_shield_validation(), + Self::NotYetValid(val) => val.as_shield_validation(), + } + } +} + +impl AsShieldBuilderViolation for MatrixRolesInCombinationBasicViolation { + fn as_shield_validation( + &self, + ) -> Option { + use MatrixRolesInCombinationBasicViolation::*; + match self { + FactorSourceNotFoundInAnyRole => { + Some(SecurityShieldBuilderInvalidReason::FactorSourceNotFoundInAnyRole) + } + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero => { + Some(SecurityShieldBuilderInvalidReason::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) + } + } + } +} +impl AsShieldBuilderViolation for MatrixRolesInCombinationForeverInvalid { + fn as_shield_validation( + &self, + ) -> Option { + use MatrixRolesInCombinationForeverInvalid::*; + match self { + RecoveryAndConfirmationFactorsOverlap => { + Some(SecurityShieldBuilderInvalidReason::RecoveryAndConfirmationFactorsOverlap) + } + } + } +} +impl AsShieldBuilderViolation for MatrixRolesInCombinationNotYetValid { + fn as_shield_validation( + &self, + ) -> Option { + use MatrixRolesInCombinationNotYetValid::*; + + match self { + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole => { + Some(SecurityShieldBuilderInvalidReason::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole) + } + } + } +} + +impl AsShieldBuilderViolation for (RoleKind, RoleBuilderValidation) { + fn as_shield_validation( + &self, + ) -> Option { + let (_, violation) = self; + match violation { + RoleBuilderValidation::BasicViolation(basic) => { + basic.as_shield_validation() + } + RoleBuilderValidation::ForeverInvalid(forever) => { + forever.as_shield_validation() + } + RoleBuilderValidation::NotYetValid(not_yet_valid) => { + not_yet_valid.as_shield_validation() + } + } + } +} + +impl AsShieldBuilderViolation for BasicViolation { + fn as_shield_validation( + &self, + ) -> Option { + use BasicViolation::*; + let reason = match self { + FactorSourceNotFound => SecurityShieldBuilderInvalidReason::FactorSourceNotFound, + RecoveryCannotSetThreshold => { + SecurityShieldBuilderInvalidReason::RecoveryCannotSetThreshold + } + ConfirmationCannotSetThreshold => { + SecurityShieldBuilderInvalidReason::ConfirmationCannotSetThreshold + } + }; + Some(reason) + } +} + +impl AsShieldBuilderViolation for ForeverInvalidReason { + fn as_shield_validation( + &self, + ) -> Option { + use ForeverInvalidReason::*; + let reason = match self { + FactorSourceAlreadyPresent => SecurityShieldBuilderInvalidReason::FactorSourceAlreadyPresent, + PrimaryCannotHaveMultipleDevices => { + SecurityShieldBuilderInvalidReason::PrimaryCannotHaveMultipleDevices + } + PrimaryCannotHavePasswordInOverrideList => { + SecurityShieldBuilderInvalidReason::PrimaryCannotHavePasswordInOverrideList + } + PrimaryCannotContainSecurityQuestions => { + SecurityShieldBuilderInvalidReason::PrimaryCannotContainSecurityQuestions + } + PrimaryCannotContainTrustedContact => { + SecurityShieldBuilderInvalidReason::PrimaryCannotContainTrustedContact + } + RecoveryRoleThresholdFactorsNotSupported => { + SecurityShieldBuilderInvalidReason::RecoveryRoleThresholdFactorsNotSupported + } + RecoveryRoleSecurityQuestionsNotSupported => { + SecurityShieldBuilderInvalidReason::RecoveryRoleSecurityQuestionsNotSupported + } + RecoveryRolePasswordNotSupported => { + SecurityShieldBuilderInvalidReason::RecoveryRolePasswordNotSupported + } + ConfirmationRoleThresholdFactorsNotSupported => { + SecurityShieldBuilderInvalidReason::ConfirmationRoleThresholdFactorsNotSupported + } + ConfirmationRoleTrustedContactNotSupported => { + SecurityShieldBuilderInvalidReason::ConfirmationRoleTrustedContactNotSupported + } + }; + Some(reason) + } +} + +impl AsShieldBuilderViolation for NotYetValidReason { + fn as_shield_validation( + &self, + ) -> Option { + use NotYetValidReason::*; + let reason = match self { + RoleMustHaveAtLeastOneFactor => SecurityShieldBuilderInvalidReason::RoleMustHaveAtLeastOneFactor, + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => { + SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor + } + PrimaryRoleWithThresholdCannotBeZeroWithFactors => { + SecurityShieldBuilderInvalidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors + } + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne => { + SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne + } + ThresholdHigherThanThresholdFactorsLen => { + SecurityShieldBuilderInvalidReason::ThresholdHigherThanThresholdFactorsLen + } + }; + Some(reason) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] +pub enum SecurityShieldBuilderInvalidReason { + #[error("Shield name is invalid")] + ShieldNameInvalid, + + #[error("The factor source was not found in any role")] + FactorSourceNotFoundInAnyRole, + + #[error("The number of days until auto confirm must be greater than zero")] + NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero, + + #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] + RecoveryAndConfirmationFactorsOverlap, + + #[error("The single factor used in the primary role must not be used in any other role")] + SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, + + // ================== + // BasicViolation + // ================== + /// e.g. tried to remove a factor source which was not found. + #[error("FactorSourceID not found")] + FactorSourceNotFound, + + #[error("Recovery cannot set threshold")] + RecoveryCannotSetThreshold, + + #[error("Confirmation cannot set threshold")] + ConfirmationCannotSetThreshold, + + // ========================= + // NotYetValidReason + // ========================= + #[error("Role must have at least one factor")] + RoleMustHaveAtLeastOneFactor, + + #[error( + "Primary role with password in threshold list must have another factor" + )] + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + + #[error( + "Primary role with threshold factors cannot have a threshold of zero" + )] + PrimaryRoleWithThresholdCannotBeZeroWithFactors, + + #[error("Primary role with password in threshold list must have threshold greater than one")] + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + + #[error("Threshold higher than threshold factors len")] + ThresholdHigherThanThresholdFactorsLen, + + // ================================ + // ForeverInvalidReason + // ================================ + #[error("Factor source already present")] + FactorSourceAlreadyPresent, + + #[error("Primary role cannot have multiple devices")] + PrimaryCannotHaveMultipleDevices, + + #[error("Primary role cannot have password in override list")] + PrimaryCannotHavePasswordInOverrideList, + + #[error("Primary role cannot contain Security Questions")] + PrimaryCannotContainSecurityQuestions, + + #[error("Primary role cannot contain Trusted Contact")] + PrimaryCannotContainTrustedContact, + + #[error("Recovery role threshold list not supported")] + RecoveryRoleThresholdFactorsNotSupported, + + #[error("Recovery role Security Questions not supported")] + RecoveryRoleSecurityQuestionsNotSupported, + + #[error("Recovery role password not supported")] + RecoveryRolePasswordNotSupported, + + #[error("Confirmation role threshold list not supported")] + ConfirmationRoleThresholdFactorsNotSupported, + + #[error("Confirmation role cannot contain Trusted Contact")] + ConfirmationRoleTrustedContactNotSupported, +} From b742d20eed1f627ec46bbc259eea8e6043a4564b Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 13:40:51 +0100 Subject: [PATCH 31/70] IMPORTANT BUG FIX: fix bug where we accidentally allowed an invalid shield with a single password factor in primary threshold. --- .../src/core/error/common_error.rs | 71 ---- .../security_structures/error_conversion.rs | 19 - .../profile/mfa/security_structures/mod.rs | 1 - ...ource_in_role_builder_validation_status.rs | 52 ++- .../security_shield_builder.rs | 62 +++- .../security_shield_builder_invalid_reason.rs | 27 +- crates/sargon/src/core/error/common_error.rs | 71 ---- .../matrices/builder/matrix_builder.rs | 79 +++-- .../builder/matrix_builder_unit_tests.rs | 15 +- .../matrices/builder/mod.rs | 2 - .../builder/violation_to_error_conversion.rs | 62 ---- .../profile/mfa/security_structures/mod.rs | 4 +- .../security_structures/roles/builder/mod.rs | 2 - .../primary_roles_builder_unit_tests.rs | 10 +- .../roles/builder/roles_builder.rs | 6 + .../builder/violation_to_error_conversion.rs | 77 ---- .../security_shield_builder.rs | 335 ++++++++++++++++-- .../security_shield_builder_invalid_reason.rs | 83 ++--- 18 files changed, 515 insertions(+), 463 deletions(-) delete mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs delete mode 100644 crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs diff --git a/crates/sargon-uniffi/src/core/error/common_error.rs b/crates/sargon-uniffi/src/core/error/common_error.rs index 20c213c7f..f79fa2c64 100644 --- a/crates/sargon-uniffi/src/core/error/common_error.rs +++ b/crates/sargon-uniffi/src/core/error/common_error.rs @@ -818,77 +818,6 @@ pub enum CommonError { #[error("Failed to encode transaction preview v2 - '{underlying}'")] FailedToEncodeTransactionPreviewV2 { underlying: String } = 10229, - - /// e.g. tried to remove a factor source which was not found. - #[error("FactorSourceID not found")] - FactorSourceNotFound = 10230, - - #[error("Recovery cannot set threshold")] - RecoveryCannotSetThreshold = 10231, - - #[error("Confirmation cannot set threshold")] - ConfirmationCannotSetThreshold = 10232, - - #[error("Factor source already present")] - FactorSourceAlreadyPresent = 10233, - - #[error("Primary role cannot have multiple devices")] - PrimaryCannotHaveMultipleDevices = 10234, - - #[error("Primary role cannot have password in override list")] - PrimaryCannotHavePasswordInOverrideList = 10235, - - #[error("Primary role cannot contain Security Questions")] - PrimaryCannotContainSecurityQuestions = 10236, - - #[error("Primary role cannot contain Trusted Contact")] - PrimaryCannotContainTrustedContact = 10237, - - #[error("Recovery role threshold list not supported")] - RecoveryRoleThresholdFactorsNotSupported = 10238, - - #[error("Recovery role Security Questions not supported")] - RecoveryRoleSecurityQuestionsNotSupported = 10239, - - #[error("Recovery role password not supported")] - RecoveryRolePasswordNotSupported = 10240, - - #[error("Confirmation role threshold list not supported")] - ConfirmationRoleThresholdFactorsNotSupported = 10241, - - #[error("Confirmation role cannot contain Trusted Contact")] - ConfirmationRoleTrustedContactNotSupported = 10242, - - #[error("Role must have at least one factor")] - RoleMustHaveAtLeastOneFactor = 10243, - - #[error( - "Primary role with password in threshold list must have another factor" - )] - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor = 10244, - - #[error( - "Primary role with threshold factors cannot have a threshold of zero" - )] - PrimaryRoleWithThresholdCannotBeZeroWithFactors = 10245, - - #[error("Primary role with password in threshold list must have threshold greater than one")] - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne = 10246, - - #[error("Threshold higher than threshold factors len")] - ThresholdHigherThanThresholdFactorsLen = 10247, - - #[error("The factor source was not found in any role")] - FactorSourceNotFoundInAnyRole = 10248, - - #[error("The number of days until auto confirm must be greater than zero")] - NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero = 10249, - - #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] - RecoveryAndConfirmationFactorsOverlap = 10250, - - #[error("The single factor used in the primary role must not be used in any other role")] - SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole = 10251, } #[uniffi::export] diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs deleted file mode 100644 index d5a785822..000000000 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/error_conversion.rs +++ /dev/null @@ -1,19 +0,0 @@ -use sargon::{MatrixBuilderValidation, RoleBuilderValidation}; - -use crate::prelude::*; - -impl From for CommonError { - fn from(value: MatrixBuilderValidation) -> Self { - let sargon_err = Into::::into(value); - Into::::into(sargon_err) - } -} - -impl From<(RoleKind, sargon::RoleBuilderValidation)> for CommonError { - fn from(value: (RoleKind, RoleBuilderValidation)) -> Self { - let (role, violation) = value; - let role = Into::::into(role); - let sargon_err = Into::::into((role, violation)); - Into::::into(sargon_err) - } -} diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index be7377e4c..d1c312969 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,4 +1,3 @@ -mod error_conversion; mod matrices; mod models; mod roles; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 244d57804..1f9ca704f 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -1,23 +1,26 @@ +use sargon::AsShieldBuilderViolation; + use crate::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Object)] pub struct FactorSourceValidationStatus { pub role: RoleKind, pub factor_source_id: FactorSourceID, - pub validation: sargon::RoleBuilderMutateResult, + pub reason_if_invalid: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Enum)] +pub enum FactorSourceValidationStatusReasonIfInvalid { + BasicViolation(String), + NonBasic(SecurityShieldBuilderInvalidReason), } #[uniffi::export] impl FactorSourceValidationStatus { - pub fn validation_err(&self) -> Option { - if let Err(e) = self - .validation - .map_err(|e| Into::::into((self.role, e))) - { - Some(e) - } else { - None - } + pub fn reason_if_invalid( + &self, + ) -> Option { + self.reason_if_invalid.clone() } pub fn role(&self) -> RoleKind { @@ -28,15 +31,40 @@ impl FactorSourceValidationStatus { self.factor_source_id.clone() } } - impl From for FactorSourceValidationStatus { fn from(val: sargon::FactorSourceInRoleBuilderValidationStatus) -> Self { + let reason_if_invalid: Option< + FactorSourceValidationStatusReasonIfInvalid, + > = { + match val.validation { + Ok(_) => None, + Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( + FactorSourceValidationStatusReasonIfInvalid::BasicViolation( + format!("{:?}", b), + ), + ), + Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v + .as_shield_validation() + .map(SecurityShieldBuilderInvalidReason::from) + .map(|x| { + FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) + }), + Err(sargon::RoleBuilderValidation::NotYetValid(v)) => ( + val.role, v, + ) + .as_shield_validation() + .map(SecurityShieldBuilderInvalidReason::from) + .map(|x| { + FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) + }), + } + }; FactorSourceValidationStatus { role: val.role.into(), factor_source_id: val.factor_source_id.into(), - validation: val.validation, + reason_if_invalid, } } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 215eba14d..bd2931344 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -133,9 +133,39 @@ impl SecurityShieldBuilder { self.set(|builder| builder.set_name(&name)); } - pub fn remove_factor(&self, factor_source_id: FactorSourceID) { + pub fn remove_factor_from_all_roles( + &self, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder + .remove_factor_from_all_roles(factor_source_id.clone().into()) + }) + } + + pub fn remove_factor_from_primary(&self, factor_source_id: FactorSourceID) { + self.set(|builder| { + builder.remove_factor_from_primary(factor_source_id.clone().into()) + }) + } + + pub fn remove_factor_from_recovery( + &self, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.remove_factor_from_recovery(factor_source_id.clone().into()) + }) + } + + pub fn remove_factor_from_confirmation( + &self, + factor_source_id: FactorSourceID, + ) { self.set(|builder| { - builder.remove_factor(factor_source_id.clone().into()) + builder.remove_factor_from_confirmation( + factor_source_id.clone().into(), + ) }) } @@ -439,7 +469,7 @@ mod tests { sut.get_primary_threshold_factors(), vec![FactorSourceID::sample_device()] ); - _ = sut.set_threshold(1); + sut.set_threshold(1); assert_eq!(sut.get_primary_threshold(), 1); sut.add_factor_source_to_primary_override( FactorSourceID::sample_arculus(), @@ -558,8 +588,24 @@ mod tests { ) ); - sut.remove_factor(FactorSourceID::sample_arculus_other()); - sut.remove_factor(FactorSourceID::sample_ledger_other()); + sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); + sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); + + let f = FactorSourceID::sample_ledger_other(); + let xs = sut.get_primary_override_factors(); + sut.add_factor_source_to_primary_override(f.clone()); + sut.remove_factor_from_primary(f.clone()); + assert_eq!(xs, sut.get_primary_override_factors()); + + let xs = sut.get_recovery_factors(); + sut.add_factor_source_to_recovery_override(f.clone()); + sut.remove_factor_from_recovery(f.clone()); + assert_eq!(xs, sut.get_recovery_factors()); + + let xs = sut.get_confirmation_factors(); + sut.add_factor_source_to_confirmation_override(f.clone()); + sut.remove_factor_from_confirmation(f.clone()); + assert_eq!(xs, sut.get_confirmation_factors()); let v0 = sut.validate(); let v1 = sut.validate(); // can call validate many times! @@ -572,15 +618,15 @@ mod tests { assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); assert_eq!( shield.matrix_of_factors.primary_role.override_factors, - vec![FactorSourceID::sample_arculus().into()] + vec![FactorSourceID::sample_arculus()] ); assert_eq!( shield.matrix_of_factors.recovery_role.override_factors, - vec![FactorSourceID::sample_ledger().into()] + vec![FactorSourceID::sample_ledger()] ); assert_eq!( shield.matrix_of_factors.confirmation_role.override_factors, - vec![FactorSourceID::sample_device().into()] + vec![FactorSourceID::sample_device()] ); } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index 519a4f88b..34dc35374 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -9,6 +9,7 @@ use thiserror::Error as ThisError; Debug, PartialEq, Eq, + Hash, InternalConversion, ThisError, uniffi::Error, @@ -17,9 +18,6 @@ pub enum SecurityShieldBuilderInvalidReason { #[error("Shield name is invalid")] ShieldNameInvalid, - #[error("The factor source was not found in any role")] - FactorSourceNotFoundInAnyRole, - #[error("The number of days until auto confirm must be greater than zero")] NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero, @@ -29,24 +27,17 @@ pub enum SecurityShieldBuilderInvalidReason { #[error("The single factor used in the primary role must not be used in any other role")] SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, - // ================== - // BasicViolation - // ================== - /// e.g. tried to remove a factor source which was not found. - #[error("FactorSourceID not found")] - FactorSourceNotFound, - - #[error("Recovery cannot set threshold")] - RecoveryCannotSetThreshold, - - #[error("Confirmation cannot set threshold")] - ConfirmationCannotSetThreshold, - // ========================= // NotYetValidReason // ========================= - #[error("Role must have at least one factor")] - RoleMustHaveAtLeastOneFactor, + #[error("PrimaryRole must have at least one factor")] + PrimaryRoleMustHaveAtLeastOneFactor, + + #[error("RecoveryRole must have at least one factor")] + RecoveryRoleMustHaveAtLeastOneFactor, + + #[error("ConfirmationRole must have at least one factor")] + ConfirmationRoleMustHaveAtLeastOneFactor, #[error( "Primary role with password in threshold list must have another factor" diff --git a/crates/sargon/src/core/error/common_error.rs b/crates/sargon/src/core/error/common_error.rs index 9b792f911..b7bc32764 100644 --- a/crates/sargon/src/core/error/common_error.rs +++ b/crates/sargon/src/core/error/common_error.rs @@ -815,77 +815,6 @@ pub enum CommonError { #[error("Failed to encode transaction preview v2 - '{underlying}'")] FailedToEncodeTransactionPreviewV2 { underlying: String } = 10229, - - /// e.g. tried to remove a factor source which was not found. - #[error("FactorSourceID not found")] - FactorSourceNotFound = 10230, - - #[error("Recovery cannot set threshold")] - RecoveryCannotSetThreshold = 10231, - - #[error("Confirmation cannot set threshold")] - ConfirmationCannotSetThreshold = 10232, - - #[error("Factor source already present")] - FactorSourceAlreadyPresent = 10233, - - #[error("Primary role cannot have multiple devices")] - PrimaryCannotHaveMultipleDevices = 10234, - - #[error("Primary role cannot have password in override list")] - PrimaryCannotHavePasswordInOverrideList = 10235, - - #[error("Primary role cannot contain Security Questions")] - PrimaryCannotContainSecurityQuestions = 10236, - - #[error("Primary role cannot contain Trusted Contact")] - PrimaryCannotContainTrustedContact = 10237, - - #[error("Recovery role threshold list not supported")] - RecoveryRoleThresholdFactorsNotSupported = 10238, - - #[error("Recovery role Security Questions not supported")] - RecoveryRoleSecurityQuestionsNotSupported = 10239, - - #[error("Recovery role password not supported")] - RecoveryRolePasswordNotSupported = 10240, - - #[error("Confirmation role threshold list not supported")] - ConfirmationRoleThresholdFactorsNotSupported = 10241, - - #[error("Confirmation role cannot contain Trusted Contact")] - ConfirmationRoleTrustedContactNotSupported = 10242, - - #[error("Role must have at least one factor")] - RoleMustHaveAtLeastOneFactor = 10243, - - #[error( - "Primary role with password in threshold list must have another factor" - )] - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor = 10244, - - #[error( - "Primary role with threshold factors cannot have a threshold of zero" - )] - PrimaryRoleWithThresholdCannotBeZeroWithFactors = 10245, - - #[error("Primary role with password in threshold list must have threshold greater than one")] - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne = 10246, - - #[error("Threshold higher than threshold factors len")] - ThresholdHigherThanThresholdFactorsLen = 10247, - - #[error("The factor source was not found in any role")] - FactorSourceNotFoundInAnyRole = 10248, - - #[error("The number of days until auto confirm must be greater than zero")] - NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero = 10249, - - #[error("Recovery and confirmation factors overlap. No factor may be used in both the recovery and confirmation roles")] - RecoveryAndConfirmationFactorsOverlap = 10250, - - #[error("The single factor used in the primary role must not be used in any other role")] - SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole = 10251, } impl CommonError { diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index 0aa6a0ac1..2f0dd4ddf 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -242,47 +242,61 @@ impl MatrixBuilder { self.number_of_days_until_auto_confirm } - /// Removes `factor_source_id` from all three roles, if not found in any an error - /// is thrown. - /// - /// # Throws - /// If none of the three role builders contains the factor source id, `Err(BasicViolation::FactorSourceNotFound)` is thrown - pub fn remove_factor( - &mut self, + fn remove_factor_from_role( + role: &mut RoleBuilder<{ ROLE }>, factor_source_id: &FactorSourceID, ) -> MatrixBuilderMutateResult { - let mut found = false; - if self - .primary_role - .remove_factor_source(factor_source_id) - .is_ok() - { - found = true; - } - if self - .recovery_role - .remove_factor_source(factor_source_id) - .is_ok() - { - found = true; - } - if self - .confirmation_role - .remove_factor_source(factor_source_id) - .is_ok() - { - found = true; - } - if !found { + if role.remove_factor_source(factor_source_id).is_ok() { + Ok(()) + } else { MatrixBuilderMutateResult::Err(MatrixBuilderValidation::CombinationViolation( MatrixRolesInCombinationViolation::Basic( MatrixRolesInCombinationBasicViolation::FactorSourceNotFoundInAnyRole, ), )) - } else { - Ok(()) } } + + pub fn remove_factor_from_primary( + &mut self, + factor_source_id: &FactorSourceID, + ) -> MatrixBuilderMutateResult { + Self::remove_factor_from_role(&mut self.primary_role, factor_source_id) + } + + pub fn remove_factor_from_recovery( + &mut self, + factor_source_id: &FactorSourceID, + ) -> MatrixBuilderMutateResult { + Self::remove_factor_from_role(&mut self.recovery_role, factor_source_id) + } + + pub fn remove_factor_from_confirmation( + &mut self, + factor_source_id: &FactorSourceID, + ) -> MatrixBuilderMutateResult { + Self::remove_factor_from_role( + &mut self.confirmation_role, + factor_source_id, + ) + } + + /// Removes `factor_source_id` from all three roles, if not found in any an error + /// is thrown. + /// + /// # Throws + /// If none of the three role builders contains the factor source id, `Err(BasicViolation::FactorSourceNotFound)` is thrown + pub fn remove_factor_from_all_roles( + &mut self, + factor_source_id: &FactorSourceID, + ) -> MatrixBuilderMutateResult { + let fsid = factor_source_id; + let r0 = self.remove_factor_from_primary(fsid); + let r1 = self.remove_factor_from_recovery(fsid); + let r2 = self.remove_factor_from_confirmation(fsid); + + r0.or(r1).or(r2) + } } // ================== @@ -371,7 +385,6 @@ impl MatrixBuilder { // is already enforced by the RoleBuilder self.validate_number_of_days_until_auto_confirm()?; - Ok(()) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs index 4ba012de0..5f64fd552 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs @@ -382,7 +382,8 @@ mod remove { #[test] fn not_found() { let mut sut = make(); - let res = sut.remove_factor(&FactorSourceID::sample_device()); + let res = + sut.remove_factor_from_all_roles(&FactorSourceID::sample_device()); assert_eq!( res, Err(MatrixBuilderValidation::CombinationViolation( @@ -400,7 +401,8 @@ mod remove { FactorSourceID::sample_device(), ) .unwrap(); - let res = sut.remove_factor(&FactorSourceID::sample_device()); + let res = + sut.remove_factor_from_all_roles(&FactorSourceID::sample_device()); assert_eq!(res, Ok(())); } @@ -411,7 +413,8 @@ mod remove { FactorSourceID::sample_device(), ) .unwrap(); - let res = sut.remove_factor(&FactorSourceID::sample_device()); + let res = + sut.remove_factor_from_primary(&FactorSourceID::sample_device()); assert_eq!(res, Ok(())); } @@ -422,7 +425,8 @@ mod remove { FactorSourceID::sample_device(), ) .unwrap(); - let res = sut.remove_factor(&FactorSourceID::sample_device()); + let res = + sut.remove_factor_from_recovery(&FactorSourceID::sample_device()); assert_eq!(res, Ok(())); } @@ -433,7 +437,8 @@ mod remove { FactorSourceID::sample_device(), ) .unwrap(); - let res = sut.remove_factor(&FactorSourceID::sample_device()); + let res = sut + .remove_factor_from_confirmation(&FactorSourceID::sample_device()); assert_eq!(res, Ok(())); } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs index 208bac4a8..4e87cc622 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/mod.rs @@ -2,10 +2,8 @@ mod error; mod matrix_builder; mod matrix_builder_unit_tests; mod matrix_template; -mod violation_to_error_conversion; pub use error::*; #[allow(unused_imports)] pub use matrix_builder::*; pub use matrix_template::*; -pub use violation_to_error_conversion::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs deleted file mode 100644 index 91df3d7ce..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/violation_to_error_conversion.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::prelude::*; - -impl From for CommonError { - fn from(value: MatrixRolesInCombinationBasicViolation) -> Self { - use MatrixRolesInCombinationBasicViolation::*; - match value { - FactorSourceNotFoundInAnyRole => { - CommonError::FactorSourceNotFoundInAnyRole - } - NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero => { - CommonError::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero - } - } - } -} -impl From for CommonError { - fn from(value: MatrixRolesInCombinationForeverInvalid) -> Self { - use MatrixRolesInCombinationForeverInvalid::*; - match value { - RecoveryAndConfirmationFactorsOverlap => { - Self::RecoveryAndConfirmationFactorsOverlap - } - } - } -} -impl From for CommonError { - fn from(value: MatrixRolesInCombinationNotYetValid) -> Self { - use MatrixRolesInCombinationNotYetValid::*; - - match value { - SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole => { - Self::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole - } - } - } -} - -impl From for CommonError { - fn from(value: MatrixRolesInCombinationViolation) -> Self { - match value { - MatrixRolesInCombinationViolation::Basic(basic) => basic.into(), - MatrixRolesInCombinationViolation::ForeverInvalid( - forever_invalid, - ) => forever_invalid.into(), - MatrixRolesInCombinationViolation::NotYetValid(not_yet_valid) => { - not_yet_valid.into() - } - } - } -} -impl From for CommonError { - fn from(value: MatrixBuilderValidation) -> Self { - match value { - MatrixBuilderValidation::RoleInIsolation { role, violation } => { - (role, violation).into() - } - MatrixBuilderValidation::CombinationViolation(violation) => { - violation.into() - } - } - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/mod.rs b/crates/sargon/src/profile/mfa/security_structures/mod.rs index fff1fff63..abf0f0d13 100644 --- a/crates/sargon/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/mod.rs @@ -2,16 +2,16 @@ mod has_role_kind; mod matrices; mod roles; mod security_shield_builder; +mod security_shield_builder_invalid_reason; mod security_structure_id; mod security_structure_metadata; mod security_structure_of_factors; -mod security_shield_builder_invalid_reason; pub use has_role_kind::*; pub use matrices::*; -pub use security_shield_builder_invalid_reason::*; pub use roles::*; pub use security_shield_builder::*; +pub use security_shield_builder_invalid_reason::*; pub use security_structure_id::*; pub use security_structure_metadata::*; pub use security_structure_of_factors::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs index cf23f6526..af9dc07f3 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/mod.rs @@ -3,7 +3,5 @@ mod primary_roles_builder_unit_tests; mod recovery_roles_builder_unit_tests; mod roles_builder; mod roles_builder_unit_tests; -mod violation_to_error_conversion; pub use roles_builder::*; -pub use violation_to_error_conversion::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs index 560aadcd5..047cd21a8 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs @@ -459,7 +459,7 @@ mod password { fn alone_is_not_ok() { // Arrange let mut sut = make(); - + let _ = sut.set_threshold(1); // Act let res = sut.add_factor_source_to_threshold(sample()); @@ -470,6 +470,14 @@ mod password { NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor ) ); + + let validation = sut.validate(); + assert_eq!( + validation, + Result::not_yet_valid( + NotYetValidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor + ) + ); } #[test] diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 198a6dc1d..dc780d1e9 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -483,6 +483,12 @@ impl RoleBuilder { } } + if self.threshold_contains_factor_source_of_kind( + FactorSourceKind::Password, + ) { + self.validation_for_addition_of_password_to_primary(Threshold)?; + } + if self.all_factors().is_empty() { return RoleBuilderMutateResult::not_yet_valid( RoleMustHaveAtLeastOneFactor, diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs deleted file mode 100644 index 25ba730a5..000000000 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/violation_to_error_conversion.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::prelude::*; - -impl From for CommonError { - fn from(value: BasicViolation) -> Self { - match value { - BasicViolation::FactorSourceNotFound => { - CommonError::FactorSourceNotFound - } - BasicViolation::RecoveryCannotSetThreshold => { - CommonError::RecoveryCannotSetThreshold - } - BasicViolation::ConfirmationCannotSetThreshold => { - CommonError::ConfirmationCannotSetThreshold - } - } - } -} -impl From for CommonError { - fn from(value: ForeverInvalidReason) -> Self { - use ForeverInvalidReason::*; - - match value { - FactorSourceAlreadyPresent => Self::FactorSourceAlreadyPresent, - PrimaryCannotHaveMultipleDevices => { - Self::PrimaryCannotHaveMultipleDevices - } - PrimaryCannotHavePasswordInOverrideList => { - Self::PrimaryCannotHavePasswordInOverrideList - } - PrimaryCannotContainSecurityQuestions => { - Self::PrimaryCannotContainSecurityQuestions - } - PrimaryCannotContainTrustedContact => { - Self::PrimaryCannotContainTrustedContact - } - RecoveryRoleThresholdFactorsNotSupported => { - Self::RecoveryRoleThresholdFactorsNotSupported - } - RecoveryRoleSecurityQuestionsNotSupported => { - Self::RecoveryRoleSecurityQuestionsNotSupported - } - RecoveryRolePasswordNotSupported => { - Self::RecoveryRolePasswordNotSupported - } - ConfirmationRoleThresholdFactorsNotSupported => { - Self::ConfirmationRoleThresholdFactorsNotSupported - } - ConfirmationRoleTrustedContactNotSupported => { - Self::ConfirmationRoleTrustedContactNotSupported - } - } - } -} -impl From for CommonError { - fn from(value: NotYetValidReason) -> Self { - use NotYetValidReason::*; - match value { - RoleMustHaveAtLeastOneFactor => { - Self::RoleMustHaveAtLeastOneFactor - }, - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => Self::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, - PrimaryRoleWithThresholdCannotBeZeroWithFactors => Self::PrimaryRoleWithThresholdCannotBeZeroWithFactors, - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne => Self::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, - ThresholdHigherThanThresholdFactorsLen => Self::ThresholdHigherThanThresholdFactorsLen - } - } -} -impl From<(RoleKind, RoleBuilderValidation)> for CommonError { - fn from(value: (RoleKind, RoleBuilderValidation)) -> Self { - let (_role, violation) = value; - match violation { - RoleBuilderValidation::BasicViolation(val) => val.into(), - RoleBuilderValidation::ForeverInvalid(val) => val.into(), - RoleBuilderValidation::NotYetValid(val) => val.into(), - } - } -} diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 67de72527..7bc4e3583 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -12,6 +12,12 @@ pub struct SecurityShieldBuilder { created_on: Timestamp, } +impl Default for SecurityShieldBuilder { + fn default() -> Self { + Self::new() + } +} + impl SecurityShieldBuilder { pub fn new() -> Self { let matrix_builder = MatrixBuilder::new(); @@ -47,11 +53,8 @@ impl SecurityShieldBuilder { ) -> IndexSet, ) -> Vec { - let input = factor_sources - .iter() - .map(|x| x.clone().into()) - .collect::>(); - self.get(|builder| call(builder, &input)) + let input = &factor_sources.into_iter().collect::>(); + self.get(|builder| call(builder, input)) .into_iter() .collect_vec() } @@ -64,10 +67,7 @@ impl SecurityShieldBuilder { ) -> Vec { self.get(|builder| { let factors = access(builder); - factors - .iter() - .map(|x| crate::FactorSourceID::from(*x)) - .collect::>() + factors.to_vec() }) } } @@ -120,9 +120,12 @@ impl SecurityShieldBuilder { factor_source_id: FactorSourceID, ) -> &Self { self.set(|builder| { - builder.add_factor_source_to_primary_threshold( - factor_source_id.clone().into(), - ) + let res = builder + .add_factor_source_to_primary_threshold(factor_source_id); + debug!( + "Add FactorSource to PrimaryRole (threshold) result: {:?}", + res + ); }) } @@ -131,15 +134,52 @@ impl SecurityShieldBuilder { factor_source_id: FactorSourceID, ) -> &Self { self.set(|builder| { - builder.add_factor_source_to_primary_override( - factor_source_id.clone().into(), - ) + let res = + builder.add_factor_source_to_primary_override(factor_source_id); + debug!( + "Add FactorSource to PrimaryRole (override) result: {:?}", + res + ); + }) + } + + /// Removes the factor from all relevant roles + pub fn remove_factor_from_all_roles( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.remove_factor_from_all_roles(&factor_source_id) + }) + } + + /// Removes factor **only** from the primary role. + pub fn remove_factor_from_primary( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.remove_factor_from_primary(&factor_source_id) + }) + } + + /// Removes factor **only** from the recovery role. + pub fn remove_factor_from_recovery( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { + self.set(|builder| { + builder.remove_factor_from_recovery(&factor_source_id) }) } - pub fn remove_factor(&self, factor_source_id: FactorSourceID) -> &Self { + /// Removes factor **only** from the confirmation role. + pub fn remove_factor_from_confirmation( + &self, + factor_source_id: FactorSourceID, + ) -> &Self { self.set(|builder| { - builder.remove_factor(&factor_source_id.clone().into()) + builder.remove_factor_from_confirmation(&factor_source_id) }) } @@ -161,9 +201,9 @@ impl SecurityShieldBuilder { factor_source_id: FactorSourceID, ) -> &Self { self.set(|builder| { - builder.add_factor_source_to_recovery_override( - factor_source_id.clone().into(), - ) + let res = builder + .add_factor_source_to_recovery_override(factor_source_id); + debug!("Add FactorSource to RecoveryRole result: {:?}", res); }) } @@ -172,9 +212,9 @@ impl SecurityShieldBuilder { factor_source_id: FactorSourceID, ) -> &Self { self.set(|builder| { - builder.add_factor_source_to_confirmation_override( - factor_source_id.clone().into(), - ) + let res = builder + .add_factor_source_to_confirmation_override(factor_source_id); + debug!("Add FactorSource to ConfirmationRole result: {:?}", res); }) } } @@ -363,6 +403,9 @@ impl SecurityShieldBuilder { impl SecurityShieldBuilder { /// `None` means valid! pub fn validate(&self) -> Option { + if !DisplayName::new(self.get_name()).is_ok() { + return Some(SecurityShieldBuilderInvalidReason::ShieldNameInvalid); + } self.get(|builder| { let r = builder.validate(); r.as_shield_validation() @@ -417,7 +460,7 @@ mod tests { #[test] fn test() { - let sut = SUT::new(); + let sut = SUT::default(); let _ = sut .set_name("S.H.I.E.L.D.") @@ -444,8 +487,8 @@ mod tests { .add_factor_source_to_confirmation_override( FactorSourceID::sample_device(), ) - .remove_factor(FactorSourceID::sample_arculus_other()) - .remove_factor(FactorSourceID::sample_ledger_other()); + .remove_factor_from_primary(FactorSourceID::sample_arculus_other()) + .remove_factor_from_recovery(FactorSourceID::sample_ledger_other()); let shield0 = sut.build().unwrap(); let shield = sut.build().unwrap(); @@ -454,18 +497,18 @@ mod tests { assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); assert_eq!( shield.matrix_of_factors.primary().get_override_factors(), - &vec![FactorSourceID::sample_arculus().into()] + &vec![FactorSourceID::sample_arculus()] ); assert_eq!( shield.matrix_of_factors.recovery().get_override_factors(), - &vec![FactorSourceID::sample_ledger().into()] + &vec![FactorSourceID::sample_ledger()] ); assert_eq!( shield .matrix_of_factors .confirmation() .get_override_factors(), - &vec![FactorSourceID::sample_device().into()] + &vec![FactorSourceID::sample_device()] ); } @@ -503,7 +546,7 @@ mod tests { assert!(!can_be(sut, FactorSourceKind::Device)); // make it valid again - sut.remove_factor(FactorSourceID::sample_device()); + sut.remove_factor_from_all_roles(FactorSourceID::sample_device()); assert!(is_fully_valid(sut, FactorSourceKind::Device)); assert!(can_be(sut, FactorSourceKind::Device)); @@ -662,3 +705,235 @@ mod tests { ); } } + +#[cfg(test)] +mod test_invalid { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = SecurityShieldBuilder; + + #[test] + fn primary_role_must_have_at_least_one_factor() { + let sut = SUT::new(); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::PrimaryRoleMustHaveAtLeastOneFactor + ); + } + + #[test] + fn primary_role_with_threshold_cannot_be_zero_with_factors() { + let sut = SUT::new(); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors + ); + } + + #[test] + fn recovery_role_must_have_at_least_one_factor() { + let sut = SUT::new(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::RecoveryRoleMustHaveAtLeastOneFactor + ); + } + + #[test] + fn confirmation_role_must_have_at_least_one_factor() { + let sut = SUT::new(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::ConfirmationRoleMustHaveAtLeastOneFactor + ); + } + + #[test] + fn valid_is_none() { + let sut = SUT::new(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ); + assert!(sut.validate().is_none()); + } + + fn valid() -> SUT { + let sut = SUT::new(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ); + sut + } + + #[test] + fn shield_name_invalid_empty() { + let sut = valid(); + sut.set_name(""); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::ShieldNameInvalid + ); + } + + #[test] + fn shield_name_truncated_if_too_long() { + let sut = valid(); + sut.set_name( + "This shield name's too long and it is going to get truncated", + ); + let shield = sut.build().unwrap(); + assert_eq!( + shield.metadata.display_name.value, + "This shield name's too long an" + ); + } + + #[test] + fn number_of_auto_confirm_days_invalid() { + let sut = valid(); + sut.set_number_of_days_until_auto_confirm(0); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero + ); + } + + #[test] + fn recovery_and_confirmation_factors_overlap() { + let sut = SUT::new(); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_device(), + ); + + let same = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_recovery_override(same); + sut.add_factor_source_to_confirmation_override( + same, // same factor! not allowed + ); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::RecoveryAndConfirmationFactorsOverlap + ); + } + + #[test] + fn single_factor_used_in_primary_must_not_be_used_in_any_other_role_in_recovery( + ) { + let sut = SUT::new(); + let same = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_override(same); + + sut.add_factor_source_to_recovery_override( + same, // same factor! not allowed + ); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole + ); + } + + #[test] + fn single_factor_used_in_primary_must_not_be_used_in_any_other_role_in_confirmation( + ) { + let sut = SUT::new(); + let same = FactorSourceID::sample_ledger(); + sut.add_factor_source_to_primary_override(same); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_arculus(), + ); + sut.add_factor_source_to_confirmation_override( + same, // same factor! not allowed + ); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole + ); + } + + #[test] + fn primary_role_with_password_in_threshold_list_must_threshold_greater_than_one( + ) { + let sut = SUT::new(); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ); + + sut.set_threshold(1); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_password(), + ); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ); + + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne + ); + } + + #[test] + fn primary_role_with_password_in_threshold_list_must_have_another_factor() { + let sut = SUT::new(); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ); + + sut.set_threshold(1); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_password(), + ); + + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor + ); + } + + #[test] + fn template() { // use this to create more tests... + let sut = valid(); + sut.set_name(""); + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::ShieldNameInvalid + ); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index f8f2e382b..bf8683583 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -1,13 +1,14 @@ use crate::prelude::*; - pub trait AsShieldBuilderViolation { fn as_shield_validation( &self, ) -> Option; } -impl AsShieldBuilderViolation for Result { +impl AsShieldBuilderViolation + for Result +{ fn as_shield_validation( &self, ) -> Option { @@ -50,9 +51,8 @@ impl AsShieldBuilderViolation for MatrixRolesInCombinationBasicViolation { ) -> Option { use MatrixRolesInCombinationBasicViolation::*; match self { - FactorSourceNotFoundInAnyRole => { - Some(SecurityShieldBuilderInvalidReason::FactorSourceNotFoundInAnyRole) - } + FactorSourceNotFoundInAnyRole => + unreachable!("Cannot happen since this error is not returned by 'validate'/'build'."), NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero => { Some(SecurityShieldBuilderInvalidReason::NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) } @@ -89,39 +89,19 @@ impl AsShieldBuilderViolation for (RoleKind, RoleBuilderValidation) { fn as_shield_validation( &self, ) -> Option { - let (_, violation) = self; + let (role_kind, violation) = self; match violation { - RoleBuilderValidation::BasicViolation(basic) => { - basic.as_shield_validation() - } + RoleBuilderValidation::BasicViolation(basic) => unreachable!("Programmer error. Should have prevented this from happening: '{:?}'", basic), RoleBuilderValidation::ForeverInvalid(forever) => { forever.as_shield_validation() } RoleBuilderValidation::NotYetValid(not_yet_valid) => { - not_yet_valid.as_shield_validation() + (*role_kind, *not_yet_valid).as_shield_validation() } } } } -impl AsShieldBuilderViolation for BasicViolation { - fn as_shield_validation( - &self, - ) -> Option { - use BasicViolation::*; - let reason = match self { - FactorSourceNotFound => SecurityShieldBuilderInvalidReason::FactorSourceNotFound, - RecoveryCannotSetThreshold => { - SecurityShieldBuilderInvalidReason::RecoveryCannotSetThreshold - } - ConfirmationCannotSetThreshold => { - SecurityShieldBuilderInvalidReason::ConfirmationCannotSetThreshold - } - }; - Some(reason) - } -} - impl AsShieldBuilderViolation for ForeverInvalidReason { fn as_shield_validation( &self, @@ -161,13 +141,28 @@ impl AsShieldBuilderViolation for ForeverInvalidReason { } } -impl AsShieldBuilderViolation for NotYetValidReason { +impl SecurityShieldBuilderInvalidReason { + pub(crate) fn role_must_have_at_least_one_factor( + role_kind: &RoleKind, + ) -> Self { + match role_kind { + RoleKind::Primary => Self::PrimaryRoleMustHaveAtLeastOneFactor, + RoleKind::Recovery => Self::RecoveryRoleMustHaveAtLeastOneFactor, + RoleKind::Confirmation => { + Self::ConfirmationRoleMustHaveAtLeastOneFactor + } + } + } +} + +impl AsShieldBuilderViolation for (RoleKind, NotYetValidReason) { fn as_shield_validation( &self, ) -> Option { + let (role_kind, violation) = self; use NotYetValidReason::*; - let reason = match self { - RoleMustHaveAtLeastOneFactor => SecurityShieldBuilderInvalidReason::RoleMustHaveAtLeastOneFactor, + let reason = match violation { + RoleMustHaveAtLeastOneFactor => SecurityShieldBuilderInvalidReason::role_must_have_at_least_one_factor(role_kind), PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => { SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor } @@ -190,9 +185,6 @@ pub enum SecurityShieldBuilderInvalidReason { #[error("Shield name is invalid")] ShieldNameInvalid, - #[error("The factor source was not found in any role")] - FactorSourceNotFoundInAnyRole, - #[error("The number of days until auto confirm must be greater than zero")] NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero, @@ -202,24 +194,17 @@ pub enum SecurityShieldBuilderInvalidReason { #[error("The single factor used in the primary role must not be used in any other role")] SingleFactorUsedInPrimaryMustNotBeUsedInAnyOtherRole, - // ================== - // BasicViolation - // ================== - /// e.g. tried to remove a factor source which was not found. - #[error("FactorSourceID not found")] - FactorSourceNotFound, - - #[error("Recovery cannot set threshold")] - RecoveryCannotSetThreshold, - - #[error("Confirmation cannot set threshold")] - ConfirmationCannotSetThreshold, - // ========================= // NotYetValidReason // ========================= - #[error("Role must have at least one factor")] - RoleMustHaveAtLeastOneFactor, + #[error("PrimaryRole must have at least one factor")] + PrimaryRoleMustHaveAtLeastOneFactor, + + #[error("RecoveryRole must have at least one factor")] + RecoveryRoleMustHaveAtLeastOneFactor, + + #[error("ConfirmationRole must have at least one factor")] + ConfirmationRoleMustHaveAtLeastOneFactor, #[error( "Primary role with password in threshold list must have another factor" From 87978800d9975db0f2eca62b45665453c887344e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 14:00:06 +0100 Subject: [PATCH 32/70] [no ci] moar tests --- .../security_shield_builder.rs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 7bc4e3583..496adb2ee 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -928,7 +928,30 @@ mod test_invalid { } #[test] - fn template() { // use this to create more tests... + fn primary_role_with_password_in_override_does_not_get_added() { + let sut = SUT::new(); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_arculus(), + ); + + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_password(), + ); + assert!(sut.get_primary_override_factors().is_empty()); // did not get added + + assert_eq!( + sut.validate().unwrap(), + SecurityShieldBuilderInvalidReason::PrimaryRoleMustHaveAtLeastOneFactor + ); + } + + #[test] + fn template() { + // use this to create more tests... let sut = valid(); sut.set_name(""); assert_eq!( From 81a04ef6de80690bcfc18d961b7d595323ef61c3 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 14:07:03 +0100 Subject: [PATCH 33/70] bump version, try fix swift. What about kotlin failures 'MethodTooLargeException'....scary. hope it fixes itself. See failing CI job 12139872550/job/33848408629 --- Cargo.lock | 4 ++-- .../Profile/MFA/SecurityShieldBuilder+Swifified.swift | 4 ++-- crates/sargon-uniffi/Cargo.toml | 2 +- crates/sargon/Cargo.toml | 2 +- .../mfa/security_structures/security_shield_builder.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d8597e9a..aebf634be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2759,7 +2759,7 @@ dependencies = [ [[package]] name = "sargon" -version = "1.1.71" +version = "1.1.72" dependencies = [ "actix-rt", "aes-gcm", @@ -2814,7 +2814,7 @@ dependencies = [ [[package]] name = "sargon-uniffi" -version = "1.1.71" +version = "1.1.72" dependencies = [ "actix-rt", "assert-json-diff", diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift index 6a0e9d031..9b4a7c939 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift @@ -5,8 +5,8 @@ extension FactorSourceValidationStatus { factorSourceId() } - public var validationError: CommonError? { - self.validationErr() + public var reasonIfInvalid: SecurityShieldBuilderInvalidReason? { + self.reasonIfInvalid() } public var role: RoleKind { diff --git a/crates/sargon-uniffi/Cargo.toml b/crates/sargon-uniffi/Cargo.toml index 1183ada80..7e2fab6c3 100644 --- a/crates/sargon-uniffi/Cargo.toml +++ b/crates/sargon-uniffi/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sargon-uniffi" # Don't forget to update version in crates/sargon/Cargo.toml -version = "1.1.71" +version = "1.1.72" edition = "2021" build = "build.rs" diff --git a/crates/sargon/Cargo.toml b/crates/sargon/Cargo.toml index 711fe419d..61dd0dd4f 100644 --- a/crates/sargon/Cargo.toml +++ b/crates/sargon/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sargon" # Don't forget to update version in crates/sargon-uniffi/Cargo.toml -version = "1.1.71" +version = "1.1.72" edition = "2021" build = "build.rs" diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 496adb2ee..0a6bae166 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -403,7 +403,7 @@ impl SecurityShieldBuilder { impl SecurityShieldBuilder { /// `None` means valid! pub fn validate(&self) -> Option { - if !DisplayName::new(self.get_name()).is_ok() { + if DisplayName::new(self.get_name()).is_err() { return Some(SecurityShieldBuilderInvalidReason::ShieldNameInvalid); } self.get(|builder| { From a36ea147186d55fdd720819a1157d7096548024b Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 14:46:35 +0100 Subject: [PATCH 34/70] Try fix MethodTooLargeException: remove field `reason_if_invalid` from FactorSourceValidationStatus --- .../MFA/SecurityShieldBuilder+Swifified.swift | 6 +- .../MFA/SecurityShieldsBuilderTests.swift | 10 +- ...ource_in_role_builder_validation_status.rs | 94 +++++++++---------- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift index 9b4a7c939..2bd1aaff6 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift @@ -5,9 +5,9 @@ extension FactorSourceValidationStatus { factorSourceId() } - public var reasonIfInvalid: SecurityShieldBuilderInvalidReason? { - self.reasonIfInvalid() - } +// public var reasonIfInvalid: SecurityShieldBuilderInvalidReason? { +// self.reasonIfInvalid() +// } public var role: RoleKind { role() diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index 47b8f23cf..92073250e 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -56,9 +56,9 @@ struct ShieldTests { #expect(builder.confirmationRoleFactors == []) } - @Test("primary override validation status trustedContact") - func primValidationStatusTrustedContact() throws { - let builder = SecurityShieldBuilder() - try #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).map(\.validationError) == [CommonError.PrimaryCannotContainTrustedContact]) - } +// @Test("primary override validation status trustedContact") +// func primValidationStatusTrustedContact() throws { +// let builder = SecurityShieldBuilder() +// try #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).map(\.validationError) == [CommonError.PrimaryCannotContainTrustedContact]) +// } } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 1f9ca704f..b02520007 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -6,65 +6,65 @@ use crate::prelude::*; pub struct FactorSourceValidationStatus { pub role: RoleKind, pub factor_source_id: FactorSourceID, - pub reason_if_invalid: Option, + // pub reason_if_invalid: Option, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Enum)] -pub enum FactorSourceValidationStatusReasonIfInvalid { - BasicViolation(String), - NonBasic(SecurityShieldBuilderInvalidReason), -} +// #[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Enum)] +// pub enum FactorSourceValidationStatusReasonIfInvalid { +// BasicViolation(String), +// NonBasic(SecurityShieldBuilderInvalidReason), +// } -#[uniffi::export] -impl FactorSourceValidationStatus { - pub fn reason_if_invalid( - &self, - ) -> Option { - self.reason_if_invalid.clone() - } +// #[uniffi::export] +// impl FactorSourceValidationStatus { +// pub fn reason_if_invalid( +// &self, +// ) -> Option { +// self.reason_if_invalid.clone() +// } - pub fn role(&self) -> RoleKind { - self.role - } +// pub fn role(&self) -> RoleKind { +// self.role +// } - pub fn factor_source_id(&self) -> FactorSourceID { - self.factor_source_id.clone() - } -} +// pub fn factor_source_id(&self) -> FactorSourceID { +// self.factor_source_id.clone() +// } +// } impl From for FactorSourceValidationStatus { fn from(val: sargon::FactorSourceInRoleBuilderValidationStatus) -> Self { - let reason_if_invalid: Option< - FactorSourceValidationStatusReasonIfInvalid, - > = { - match val.validation { - Ok(_) => None, - Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( - FactorSourceValidationStatusReasonIfInvalid::BasicViolation( - format!("{:?}", b), - ), - ), - Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v - .as_shield_validation() - .map(SecurityShieldBuilderInvalidReason::from) - .map(|x| { - FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) - }), - Err(sargon::RoleBuilderValidation::NotYetValid(v)) => ( - val.role, v, - ) - .as_shield_validation() - .map(SecurityShieldBuilderInvalidReason::from) - .map(|x| { - FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) - }), - } - }; + // let reason_if_invalid: Option< + // FactorSourceValidationStatusReasonIfInvalid, + // > = { + // match val.validation { + // Ok(_) => None, + // Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( + // FactorSourceValidationStatusReasonIfInvalid::BasicViolation( + // format!("{:?}", b), + // ), + // ), + // Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v + // .as_shield_validation() + // .map(SecurityShieldBuilderInvalidReason::from) + // .map(|x| { + // FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) + // }), + // Err(sargon::RoleBuilderValidation::NotYetValid(v)) => ( + // val.role, v, + // ) + // .as_shield_validation() + // .map(SecurityShieldBuilderInvalidReason::from) + // .map(|x| { + // FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) + // }), + // } + // }; FactorSourceValidationStatus { role: val.role.into(), factor_source_id: val.factor_source_id.into(), - reason_if_invalid, + // reason_if_invalid, } } } From a18cf2d17a4fb5e63aef2d1c0e9aaeb761e7cc1a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 15:01:37 +0100 Subject: [PATCH 35/70] Try to fix MethodTooLargeException by adding repr(u32) to SecurityShieldBuilderInvalidReason --- .../security_shield_builder.rs | 117 +++++++++--------- .../security_shield_builder_invalid_reason.rs | 23 ++-- 2 files changed, 74 insertions(+), 66 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index bd2931344..c5e435371 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -531,63 +531,66 @@ mod tests { sut.get_confirmation_factors(), vec![FactorSourceID::sample_device(),] ); - - assert_ne!( - sim_prim, - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - FactorSourceID::sample_arculus(), - ]) - ); - - assert_ne!( - sim_prim_threshold, - sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - FactorSourceID::sample_arculus() - ]) - ); - - assert_ne!( - sim_rec, - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - FactorSourceID::sample_ledger(), - ]) - ); - - assert_ne!( - sim_conf, - sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - FactorSourceID::sample_device(), - ]) - ); - - assert_ne!( - sim_kind_prim, - sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - FactorSourceKind::Device, - ) - ); - - assert_ne!( - sim_kind_prim_threshold, - sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - FactorSourceKind::Device, - ) - ); - - assert_eq!( - sim_kind_rec, - sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - FactorSourceKind::ArculusCard, - ) - ); - - assert_eq!( - sim_kind_conf, - sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - FactorSourceKind::ArculusCard, - ) - ); - + /* + + Reintroduce these asserts when we reintroduce the field `reason_if_invalid` inside `FactorSourceValidationStatus` + + assert_ne!( + sim_prim, + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]) + ); + + assert_ne!( + sim_prim_threshold, + sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus() + ]) + ); + + assert_ne!( + sim_rec, + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]) + ); + + assert_ne!( + sim_conf, + sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]) + ); + + assert_ne!( + sim_kind_prim, + sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + FactorSourceKind::Device, + ) + ); + + assert_ne!( + sim_kind_prim_threshold, + sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + FactorSourceKind::Device, + ) + ); + + assert_eq!( + sim_kind_rec, + sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::ArculusCard, + ) + ); + + assert_eq!( + sim_kind_conf, + sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::ArculusCard, + ) + ); + */ sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index 34dc35374..b1c56d892 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -3,16 +3,21 @@ use sargon::SecurityShieldBuilderInvalidReason as InternalSecurityShieldBuilderI use thiserror::Error as ThisError; +// #[derive( +// Clone, +// Copy, +// Debug, +// PartialEq, +// Eq, +// Hash, +// InternalConversion, +// ThisError, +// uniffi::Error, +// )] + +#[repr(u32)] #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - InternalConversion, - ThisError, - uniffi::Error, + Clone, Debug, ThisError, PartialEq, InternalConversion, uniffi::Error, )] pub enum SecurityShieldBuilderInvalidReason { #[error("Shield name is invalid")] From 16086cee8d146c22d25263a2c09a1db7dd2ecdf4 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 15:22:48 +0100 Subject: [PATCH 36/70] Try to fix `MethodTooLargeException` by temporarily not compiling the SecurityShieldBuilder in sargon-uniffi - also modified `SecurityShieldBuilderInvalidReason` to be repr(u32). --- .../profile/mfa/security_structures/mod.rs | 2 +- .../security_shield_builder.rs | 1270 ++++++++--------- .../security_shield_builder_invalid_reason.rs | 5 +- .../os/storage/KeystoreAccessRequestTest.kt | 48 +- 4 files changed, 664 insertions(+), 661 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index d1c312969..3a43df05b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,7 +1,7 @@ mod matrices; mod models; mod roles; -mod security_shield_builder; +// mod security_shield_builder; mod security_shield_builder_invalid_reason; mod security_structure_id; mod security_structure_metadata; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index c5e435371..65652137b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -1,635 +1,635 @@ -#![allow(clippy::new_without_default)] -#![allow(dead_code)] -#![allow(unused_variables)] - -use std::{ - borrow::Borrow, - sync::{Arc, RwLock}, -}; - -use sargon::{IndexSet, MatrixBuilder}; - -use crate::prelude::*; - -/// A builder of `SecurityStructureOfFactorSourceIds` a.k.a. `SecurityShield`, -/// which contains a MatrixOfFactorSourceIds - with primary, recovery, and -/// confirmation roles. -#[derive(Debug, uniffi::Object)] -pub struct SecurityShieldBuilder { - wrapped: RwLock, -} - -#[uniffi::export] -impl SecurityShieldBuilder { - #[uniffi::constructor] - pub fn new() -> Arc { - Arc::new(Self { - wrapped: RwLock::new(sargon::SecurityShieldBuilder::new()), - }) - } -} - -impl SecurityShieldBuilder { - fn get( - &self, - access: impl Fn(&sargon::SecurityShieldBuilder) -> R, - ) -> R { - let binding = self.wrapped.read().unwrap(); - access(&binding) - } - - fn set( - &self, - mut write: impl FnMut( - &mut sargon::SecurityShieldBuilder, - ) -> &sargon::SecurityShieldBuilder, - ) { - let mut binding = self.wrapped.write().expect("No poison"); - _ = write(&mut binding); - } - - fn validation_for_addition_of_factor_source_by_calling( - &self, - factor_sources: Vec, - call: impl Fn( - &sargon::SecurityShieldBuilder, - Vec, - ) - -> Vec, - ) -> Vec> { - let input = factor_sources - .clone() - .into_iter() - .map(Into::::into) - .collect_vec(); - - self.get(|builder| { - call(builder, input.clone()) - .into_iter() - .map(Into::::into) - .map(Arc::new) - .collect() - }) - } -} - -impl SecurityShieldBuilder { - fn get_factors( - &self, - access: impl Fn( - &sargon::SecurityShieldBuilder, - ) -> Vec, - ) -> Vec { - self.get(|builder| { - let factors = access(builder); - factors - .into_iter() - .map(crate::FactorSourceID::from) - .collect::>() - }) - } -} - -// ==================== -// ==== GET / READ ==== -// ==================== -#[uniffi::export] -impl SecurityShieldBuilder { - pub fn get_primary_threshold(&self) -> u8 { - self.get(|builder| builder.get_threshold()) - } - - pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { - self.get(|builder| builder.get_number_of_days_until_auto_confirm()) - } - - pub fn get_name(&self) -> String { - self.get(|builder| builder.get_name()) - } - - pub fn get_primary_threshold_factors(&self) -> Vec { - self.get_factors(|builder| builder.get_primary_threshold_factors()) - } - - pub fn get_primary_override_factors(&self) -> Vec { - self.get_factors(|builder| builder.get_primary_override_factors()) - } - - pub fn get_recovery_factors(&self) -> Vec { - self.get_factors(|builder| builder.get_recovery_factors()) - } - - pub fn get_confirmation_factors(&self) -> Vec { - self.get_factors(|builder| builder.get_confirmation_factors()) - } -} - -// ==================== -// ===== MUTATION ===== -// ==================== -#[uniffi::export] -impl SecurityShieldBuilder { - pub fn set_name(&self, name: String) { - self.set(|builder| builder.set_name(&name)); - } - - pub fn remove_factor_from_all_roles( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder - .remove_factor_from_all_roles(factor_source_id.clone().into()) - }) - } - - pub fn remove_factor_from_primary(&self, factor_source_id: FactorSourceID) { - self.set(|builder| { - builder.remove_factor_from_primary(factor_source_id.clone().into()) - }) - } - - pub fn remove_factor_from_recovery( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder.remove_factor_from_recovery(factor_source_id.clone().into()) - }) - } - - pub fn remove_factor_from_confirmation( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder.remove_factor_from_confirmation( - factor_source_id.clone().into(), - ) - }) - } - - pub fn set_threshold(&self, threshold: u8) { - self.set(|builder| builder.set_threshold(threshold)) - } - - pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { - self.set(|builder| { - builder.set_number_of_days_until_auto_confirm(number_of_days) - }) - } - - /// Adds the factor source to the primary role threshold list. - pub fn add_factor_source_to_primary_threshold( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder.add_factor_source_to_primary_threshold( - factor_source_id.clone().into(), - ) - }) - } - - pub fn add_factor_source_to_primary_override( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder.add_factor_source_to_primary_override( - factor_source_id.clone().into(), - ) - }) - } - - pub fn add_factor_source_to_recovery_override( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder.add_factor_source_to_recovery_override( - factor_source_id.clone().into(), - ) - }) - } - - pub fn add_factor_source_to_confirmation_override( - &self, - factor_source_id: FactorSourceID, - ) { - self.set(|builder| { - builder.add_factor_source_to_confirmation_override( - factor_source_id.clone().into(), - ) - }) - } -} - -#[uniffi::export] -impl SecurityShieldBuilder { - pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - factor_source_kind.clone().into(), - ) - ) - } - - pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( - factor_source_kind.clone().into(), - ) - ) - } - - pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - factor_source_kind.clone().into(), - ) - ) - } - - pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( - factor_source_kind.clone().into(), - ) - ) - } - - pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( - factor_source_kind.clone().into(), - ) - ) - } - - pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| { - builder - .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - factor_source_kind.clone().into(), - ) - }) - } - - pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( - factor_source_kind.clone().into(), - ) - ) - } - - pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.get(|builder| - builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - factor_source_kind.clone().into(), - ) - ) - } -} - -#[uniffi::export] -impl SecurityShieldBuilder { - pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( - &self, - factor_sources: Vec, - ) -> Vec> { - self.validation_for_addition_of_factor_source_by_calling( - factor_sources, - |builder, input| { - builder - .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) - }, - ) - } - - pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( - &self, - factor_sources: Vec, - ) -> Vec> { - self.validation_for_addition_of_factor_source_by_calling( - factor_sources, - |builder, input| { - builder.validation_for_addition_of_factor_source_to_primary_override_for_each(input) - }, - ) - } - - pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( - &self, - factor_sources: Vec, - ) -> Vec> { - self.validation_for_addition_of_factor_source_by_calling( - factor_sources, - |builder, input| { - builder - .validation_for_addition_of_factor_source_to_recovery_override_for_each(input) - }, - ) - } - - pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( - &self, - factor_sources: Vec, - ) -> Vec> { - self.validation_for_addition_of_factor_source_by_calling( - factor_sources, - |builder, input| { - builder.validation_for_addition_of_factor_source_to_confirmation_override_for_each( - input, - ) - }, - ) - } -} - -#[uniffi::export] -impl SecurityShieldBuilder { - pub fn validate(&self) -> Option { - self.get(|builder| builder.validate().map(|x| x.into())) - } - - pub fn build( - &self, - ) -> Result< - SecurityStructureOfFactorSourceIDs, - SecurityShieldBuilderInvalidReason, - > { - self.get(|builder| builder.build()) - .map(|shield| shield.into()) - .map_err(|x| x.into()) - } -} - -impl FactorSourceID { - pub fn new(inner: impl Borrow) -> Self { - Self::from(*inner.borrow()) - } -} - -#[cfg(test)] -impl FactorSourceID { - pub fn sample_device() -> Self { - Self::new(sargon::FactorSourceID::sample_device()) - } - - pub fn sample_device_other() -> Self { - Self::new(sargon::FactorSourceID::sample_device_other()) - } - - pub fn sample_ledger() -> Self { - Self::new(sargon::FactorSourceID::sample_ledger()) - } - - pub fn sample_ledger_other() -> Self { - Self::new(sargon::FactorSourceID::sample_ledger_other()) - } - - pub fn sample_arculus() -> Self { - Self::new(sargon::FactorSourceID::sample_arculus()) - } - - pub fn sample_arculus_other() -> Self { - Self::new(sargon::FactorSourceID::sample_arculus_other()) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = SecurityShieldBuilder; - - #[test] - fn test() { - let sut = SUT::new(); - - assert_eq!(sut.get_name(), "My Shield"); - sut.set_name("S.H.I.E.L.D.".to_owned()); - - assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); - sut.set_number_of_days_until_auto_confirm(u16::MAX); - assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); - // Primary - let sim_prim = - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - FactorSourceID::sample_arculus(), - ]); - - let sim_prim_threshold = sut - .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - FactorSourceID::sample_arculus(), - ]); - - let sim_kind_prim = sut - .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - FactorSourceKind::Device, - ); - - let sim_kind_prim_threshold = sut - .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - FactorSourceKind::Device, - ); - - sut.add_factor_source_to_primary_threshold( - FactorSourceID::sample_device(), - ); - assert_eq!( - sut.get_primary_threshold_factors(), - vec![FactorSourceID::sample_device()] - ); - sut.set_threshold(1); - assert_eq!(sut.get_primary_threshold(), 1); - sut.add_factor_source_to_primary_override( - FactorSourceID::sample_arculus(), - ); - sut.add_factor_source_to_primary_override( - FactorSourceID::sample_arculus_other(), - ); - - assert_eq!( - sut.get_primary_override_factors(), - vec![ - FactorSourceID::sample_arculus(), - FactorSourceID::sample_arculus_other() - ] - ); - - // Recovery - let sim_rec = - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - FactorSourceID::sample_ledger(), - ]); - - let sim_kind_rec = sut - .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - FactorSourceKind::ArculusCard, - ); - - sut.add_factor_source_to_recovery_override( - FactorSourceID::sample_ledger(), - ); - sut.add_factor_source_to_recovery_override( - FactorSourceID::sample_ledger_other(), - ); - - assert_eq!( - sut.get_recovery_factors(), - vec![ - FactorSourceID::sample_ledger(), - FactorSourceID::sample_ledger_other() - ] - ); - - // Confirmation - let sim_conf = sut - .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - FactorSourceID::sample_device(), - ]); - - let sim_kind_conf = sut - .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - FactorSourceKind::ArculusCard, - ); - - sut.add_factor_source_to_confirmation_override( - FactorSourceID::sample_device(), - ); - - assert_eq!( - sut.get_confirmation_factors(), - vec![FactorSourceID::sample_device(),] - ); - /* - - Reintroduce these asserts when we reintroduce the field `reason_if_invalid` inside `FactorSourceValidationStatus` - - assert_ne!( - sim_prim, - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - FactorSourceID::sample_arculus(), - ]) - ); - - assert_ne!( - sim_prim_threshold, - sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - FactorSourceID::sample_arculus() - ]) - ); - - assert_ne!( - sim_rec, - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - FactorSourceID::sample_ledger(), - ]) - ); - - assert_ne!( - sim_conf, - sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - FactorSourceID::sample_device(), - ]) - ); - - assert_ne!( - sim_kind_prim, - sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - FactorSourceKind::Device, - ) - ); - - assert_ne!( - sim_kind_prim_threshold, - sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - FactorSourceKind::Device, - ) - ); - - assert_eq!( - sim_kind_rec, - sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - FactorSourceKind::ArculusCard, - ) - ); - - assert_eq!( - sim_kind_conf, - sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - FactorSourceKind::ArculusCard, - ) - ); - */ - sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); - sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); - - let f = FactorSourceID::sample_ledger_other(); - let xs = sut.get_primary_override_factors(); - sut.add_factor_source_to_primary_override(f.clone()); - sut.remove_factor_from_primary(f.clone()); - assert_eq!(xs, sut.get_primary_override_factors()); - - let xs = sut.get_recovery_factors(); - sut.add_factor_source_to_recovery_override(f.clone()); - sut.remove_factor_from_recovery(f.clone()); - assert_eq!(xs, sut.get_recovery_factors()); - - let xs = sut.get_confirmation_factors(); - sut.add_factor_source_to_confirmation_override(f.clone()); - sut.remove_factor_from_confirmation(f.clone()); - assert_eq!(xs, sut.get_confirmation_factors()); - - let v0 = sut.validate(); - let v1 = sut.validate(); // can call validate many times! - assert_eq!(v0, v1); - - let shield0 = sut.build().unwrap(); - let shield = sut.build().unwrap(); // can call build many times! - assert_eq!(shield0, shield); - - assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); - assert_eq!( - shield.matrix_of_factors.primary_role.override_factors, - vec![FactorSourceID::sample_arculus()] - ); - assert_eq!( - shield.matrix_of_factors.recovery_role.override_factors, - vec![FactorSourceID::sample_ledger()] - ); - assert_eq!( - shield.matrix_of_factors.confirmation_role.override_factors, - vec![FactorSourceID::sample_device()] - ); - } -} +// #![allow(clippy::new_without_default)] +// #![allow(dead_code)] +// #![allow(unused_variables)] + +// use std::{ +// borrow::Borrow, +// sync::{Arc, RwLock}, +// }; + +// use sargon::{IndexSet, MatrixBuilder}; + +// use crate::prelude::*; + +// /// A builder of `SecurityStructureOfFactorSourceIds` a.k.a. `SecurityShield`, +// /// which contains a MatrixOfFactorSourceIds - with primary, recovery, and +// /// confirmation roles. +// #[derive(Debug, uniffi::Object)] +// pub struct SecurityShieldBuilder { +// wrapped: RwLock, +// } + +// #[uniffi::export] +// impl SecurityShieldBuilder { +// #[uniffi::constructor] +// pub fn new() -> Arc { +// Arc::new(Self { +// wrapped: RwLock::new(sargon::SecurityShieldBuilder::new()), +// }) +// } +// } + +// impl SecurityShieldBuilder { +// fn get( +// &self, +// access: impl Fn(&sargon::SecurityShieldBuilder) -> R, +// ) -> R { +// let binding = self.wrapped.read().unwrap(); +// access(&binding) +// } + +// fn set( +// &self, +// mut write: impl FnMut( +// &mut sargon::SecurityShieldBuilder, +// ) -> &sargon::SecurityShieldBuilder, +// ) { +// let mut binding = self.wrapped.write().expect("No poison"); +// _ = write(&mut binding); +// } + +// fn validation_for_addition_of_factor_source_by_calling( +// &self, +// factor_sources: Vec, +// call: impl Fn( +// &sargon::SecurityShieldBuilder, +// Vec, +// ) +// -> Vec, +// ) -> Vec> { +// let input = factor_sources +// .clone() +// .into_iter() +// .map(Into::::into) +// .collect_vec(); + +// self.get(|builder| { +// call(builder, input.clone()) +// .into_iter() +// .map(Into::::into) +// .map(Arc::new) +// .collect() +// }) +// } +// } + +// impl SecurityShieldBuilder { +// fn get_factors( +// &self, +// access: impl Fn( +// &sargon::SecurityShieldBuilder, +// ) -> Vec, +// ) -> Vec { +// self.get(|builder| { +// let factors = access(builder); +// factors +// .into_iter() +// .map(crate::FactorSourceID::from) +// .collect::>() +// }) +// } +// } + +// // ==================== +// // ==== GET / READ ==== +// // ==================== +// #[uniffi::export] +// impl SecurityShieldBuilder { +// pub fn get_primary_threshold(&self) -> u8 { +// self.get(|builder| builder.get_threshold()) +// } + +// pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { +// self.get(|builder| builder.get_number_of_days_until_auto_confirm()) +// } + +// pub fn get_name(&self) -> String { +// self.get(|builder| builder.get_name()) +// } + +// pub fn get_primary_threshold_factors(&self) -> Vec { +// self.get_factors(|builder| builder.get_primary_threshold_factors()) +// } + +// pub fn get_primary_override_factors(&self) -> Vec { +// self.get_factors(|builder| builder.get_primary_override_factors()) +// } + +// pub fn get_recovery_factors(&self) -> Vec { +// self.get_factors(|builder| builder.get_recovery_factors()) +// } + +// pub fn get_confirmation_factors(&self) -> Vec { +// self.get_factors(|builder| builder.get_confirmation_factors()) +// } +// } + +// // ==================== +// // ===== MUTATION ===== +// // ==================== +// #[uniffi::export] +// impl SecurityShieldBuilder { +// pub fn set_name(&self, name: String) { +// self.set(|builder| builder.set_name(&name)); +// } + +// pub fn remove_factor_from_all_roles( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder +// .remove_factor_from_all_roles(factor_source_id.clone().into()) +// }) +// } + +// pub fn remove_factor_from_primary(&self, factor_source_id: FactorSourceID) { +// self.set(|builder| { +// builder.remove_factor_from_primary(factor_source_id.clone().into()) +// }) +// } + +// pub fn remove_factor_from_recovery( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder.remove_factor_from_recovery(factor_source_id.clone().into()) +// }) +// } + +// pub fn remove_factor_from_confirmation( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder.remove_factor_from_confirmation( +// factor_source_id.clone().into(), +// ) +// }) +// } + +// pub fn set_threshold(&self, threshold: u8) { +// self.set(|builder| builder.set_threshold(threshold)) +// } + +// pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { +// self.set(|builder| { +// builder.set_number_of_days_until_auto_confirm(number_of_days) +// }) +// } + +// /// Adds the factor source to the primary role threshold list. +// pub fn add_factor_source_to_primary_threshold( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder.add_factor_source_to_primary_threshold( +// factor_source_id.clone().into(), +// ) +// }) +// } + +// pub fn add_factor_source_to_primary_override( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder.add_factor_source_to_primary_override( +// factor_source_id.clone().into(), +// ) +// }) +// } + +// pub fn add_factor_source_to_recovery_override( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder.add_factor_source_to_recovery_override( +// factor_source_id.clone().into(), +// ) +// }) +// } + +// pub fn add_factor_source_to_confirmation_override( +// &self, +// factor_source_id: FactorSourceID, +// ) { +// self.set(|builder| { +// builder.add_factor_source_to_confirmation_override( +// factor_source_id.clone().into(), +// ) +// }) +// } +// } + +// #[uniffi::export] +// impl SecurityShieldBuilder { +// pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( +// factor_source_kind.clone().into(), +// ) +// ) +// } + +// pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( +// factor_source_kind.clone().into(), +// ) +// ) +// } + +// pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( +// factor_source_kind.clone().into(), +// ) +// ) +// } + +// pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( +// factor_source_kind.clone().into(), +// ) +// ) +// } + +// pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( +// factor_source_kind.clone().into(), +// ) +// ) +// } + +// pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| { +// builder +// .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( +// factor_source_kind.clone().into(), +// ) +// }) +// } + +// pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( +// factor_source_kind.clone().into(), +// ) +// ) +// } + +// pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( +// &self, +// factor_source_kind: FactorSourceKind, +// ) -> bool { +// self.get(|builder| +// builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( +// factor_source_kind.clone().into(), +// ) +// ) +// } +// } + +// #[uniffi::export] +// impl SecurityShieldBuilder { +// pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( +// &self, +// factor_sources: Vec, +// ) -> Vec> { +// self.validation_for_addition_of_factor_source_by_calling( +// factor_sources, +// |builder, input| { +// builder +// .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) +// }, +// ) +// } + +// pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( +// &self, +// factor_sources: Vec, +// ) -> Vec> { +// self.validation_for_addition_of_factor_source_by_calling( +// factor_sources, +// |builder, input| { +// builder.validation_for_addition_of_factor_source_to_primary_override_for_each(input) +// }, +// ) +// } + +// pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( +// &self, +// factor_sources: Vec, +// ) -> Vec> { +// self.validation_for_addition_of_factor_source_by_calling( +// factor_sources, +// |builder, input| { +// builder +// .validation_for_addition_of_factor_source_to_recovery_override_for_each(input) +// }, +// ) +// } + +// pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( +// &self, +// factor_sources: Vec, +// ) -> Vec> { +// self.validation_for_addition_of_factor_source_by_calling( +// factor_sources, +// |builder, input| { +// builder.validation_for_addition_of_factor_source_to_confirmation_override_for_each( +// input, +// ) +// }, +// ) +// } +// } + +// #[uniffi::export] +// impl SecurityShieldBuilder { +// pub fn validate(&self) -> Option { +// self.get(|builder| builder.validate().map(|x| x.into())) +// } + +// pub fn build( +// &self, +// ) -> Result< +// SecurityStructureOfFactorSourceIDs, +// SecurityShieldBuilderInvalidReason, +// > { +// self.get(|builder| builder.build()) +// .map(|shield| shield.into()) +// .map_err(|x| x.into()) +// } +// } + +// impl FactorSourceID { +// pub fn new(inner: impl Borrow) -> Self { +// Self::from(*inner.borrow()) +// } +// } + +// #[cfg(test)] +// impl FactorSourceID { +// pub fn sample_device() -> Self { +// Self::new(sargon::FactorSourceID::sample_device()) +// } + +// pub fn sample_device_other() -> Self { +// Self::new(sargon::FactorSourceID::sample_device_other()) +// } + +// pub fn sample_ledger() -> Self { +// Self::new(sargon::FactorSourceID::sample_ledger()) +// } + +// pub fn sample_ledger_other() -> Self { +// Self::new(sargon::FactorSourceID::sample_ledger_other()) +// } + +// pub fn sample_arculus() -> Self { +// Self::new(sargon::FactorSourceID::sample_arculus()) +// } + +// pub fn sample_arculus_other() -> Self { +// Self::new(sargon::FactorSourceID::sample_arculus_other()) +// } +// } + +// #[cfg(test)] +// mod tests { + +// use super::*; + +// #[allow(clippy::upper_case_acronyms)] +// type SUT = SecurityShieldBuilder; + +// #[test] +// fn test() { +// let sut = SUT::new(); + +// assert_eq!(sut.get_name(), "My Shield"); +// sut.set_name("S.H.I.E.L.D.".to_owned()); + +// assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); +// sut.set_number_of_days_until_auto_confirm(u16::MAX); +// assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); +// // Primary +// let sim_prim = +// sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ +// FactorSourceID::sample_arculus(), +// ]); + +// let sim_prim_threshold = sut +// .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ +// FactorSourceID::sample_arculus(), +// ]); + +// let sim_kind_prim = sut +// .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( +// FactorSourceKind::Device, +// ); + +// let sim_kind_prim_threshold = sut +// .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( +// FactorSourceKind::Device, +// ); + +// sut.add_factor_source_to_primary_threshold( +// FactorSourceID::sample_device(), +// ); +// assert_eq!( +// sut.get_primary_threshold_factors(), +// vec![FactorSourceID::sample_device()] +// ); +// sut.set_threshold(1); +// assert_eq!(sut.get_primary_threshold(), 1); +// sut.add_factor_source_to_primary_override( +// FactorSourceID::sample_arculus(), +// ); +// sut.add_factor_source_to_primary_override( +// FactorSourceID::sample_arculus_other(), +// ); + +// assert_eq!( +// sut.get_primary_override_factors(), +// vec![ +// FactorSourceID::sample_arculus(), +// FactorSourceID::sample_arculus_other() +// ] +// ); + +// // Recovery +// let sim_rec = +// sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ +// FactorSourceID::sample_ledger(), +// ]); + +// let sim_kind_rec = sut +// .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( +// FactorSourceKind::ArculusCard, +// ); + +// sut.add_factor_source_to_recovery_override( +// FactorSourceID::sample_ledger(), +// ); +// sut.add_factor_source_to_recovery_override( +// FactorSourceID::sample_ledger_other(), +// ); + +// assert_eq!( +// sut.get_recovery_factors(), +// vec![ +// FactorSourceID::sample_ledger(), +// FactorSourceID::sample_ledger_other() +// ] +// ); + +// // Confirmation +// let sim_conf = sut +// .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ +// FactorSourceID::sample_device(), +// ]); + +// let sim_kind_conf = sut +// .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( +// FactorSourceKind::ArculusCard, +// ); + +// sut.add_factor_source_to_confirmation_override( +// FactorSourceID::sample_device(), +// ); + +// assert_eq!( +// sut.get_confirmation_factors(), +// vec![FactorSourceID::sample_device(),] +// ); +// /* + +// Reintroduce these asserts when we reintroduce the field `reason_if_invalid` inside `FactorSourceValidationStatus` + +// assert_ne!( +// sim_prim, +// sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ +// FactorSourceID::sample_arculus(), +// ]) +// ); + +// assert_ne!( +// sim_prim_threshold, +// sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ +// FactorSourceID::sample_arculus() +// ]) +// ); + +// assert_ne!( +// sim_rec, +// sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ +// FactorSourceID::sample_ledger(), +// ]) +// ); + +// assert_ne!( +// sim_conf, +// sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ +// FactorSourceID::sample_device(), +// ]) +// ); + +// assert_ne!( +// sim_kind_prim, +// sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( +// FactorSourceKind::Device, +// ) +// ); + +// assert_ne!( +// sim_kind_prim_threshold, +// sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( +// FactorSourceKind::Device, +// ) +// ); + +// assert_eq!( +// sim_kind_rec, +// sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( +// FactorSourceKind::ArculusCard, +// ) +// ); + +// assert_eq!( +// sim_kind_conf, +// sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( +// FactorSourceKind::ArculusCard, +// ) +// ); +// */ +// sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); +// sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); + +// let f = FactorSourceID::sample_ledger_other(); +// let xs = sut.get_primary_override_factors(); +// sut.add_factor_source_to_primary_override(f.clone()); +// sut.remove_factor_from_primary(f.clone()); +// assert_eq!(xs, sut.get_primary_override_factors()); + +// let xs = sut.get_recovery_factors(); +// sut.add_factor_source_to_recovery_override(f.clone()); +// sut.remove_factor_from_recovery(f.clone()); +// assert_eq!(xs, sut.get_recovery_factors()); + +// let xs = sut.get_confirmation_factors(); +// sut.add_factor_source_to_confirmation_override(f.clone()); +// sut.remove_factor_from_confirmation(f.clone()); +// assert_eq!(xs, sut.get_confirmation_factors()); + +// let v0 = sut.validate(); +// let v1 = sut.validate(); // can call validate many times! +// assert_eq!(v0, v1); + +// let shield0 = sut.build().unwrap(); +// let shield = sut.build().unwrap(); // can call build many times! +// assert_eq!(shield0, shield); + +// assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); +// assert_eq!( +// shield.matrix_of_factors.primary_role.override_factors, +// vec![FactorSourceID::sample_arculus()] +// ); +// assert_eq!( +// shield.matrix_of_factors.recovery_role.override_factors, +// vec![FactorSourceID::sample_ledger()] +// ); +// assert_eq!( +// shield.matrix_of_factors.confirmation_role.override_factors, +// vec![FactorSourceID::sample_device()] +// ); +// } +// } diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index bf8683583..f594173a5 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -180,7 +180,10 @@ impl AsShieldBuilderViolation for (RoleKind, NotYetValidReason) { } } -#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] +// #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] + +#[repr(u32)] +#[derive(Clone, Debug, thiserror::Error, PartialEq)] pub enum SecurityShieldBuilderInvalidReason { #[error("Shield name is invalid")] ShieldNameInvalid, diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/storage/KeystoreAccessRequestTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/storage/KeystoreAccessRequestTest.kt index 596ab18df..ffe3a2367 100644 --- a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/storage/KeystoreAccessRequestTest.kt +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/os/storage/KeystoreAccessRequestTest.kt @@ -48,29 +48,29 @@ class KeystoreAccessRequestTest { } } - @Test - fun testSpecsOfMnemonic() = runTest { - val request = KeystoreAccessRequest.ForMnemonic( - onRequestAuthorization = { Result.success(Unit) } - ) - assertInstanceOf(KeySpec.Mnemonic::class.java, request.keySpec) - - try { - request.requestAuthorization().getOrThrow() - } catch (exception: Exception) { - assert(false) { "requestAuthorization for Mnemonic should succeed but didn't" } - } - - val failingRequest = KeystoreAccessRequest.ForMnemonic( - onRequestAuthorization = { Result.failure(RuntimeException("An error")) } - ) - - try { - failingRequest.requestAuthorization().getOrThrow() - assert(false) { "requestAuthorization for failing access to Mnemonic should fail but succeeded" } - } catch (exception: Exception) { - assert(true) { "requestAuthorization for failing access to Mnemonic should fail" } - } - } + // @Test + // fun testSpecsOfMnemonic() = runTest { + // val request = KeystoreAccessRequest.ForMnemonic( + // onRequestAuthorization = { Result.success(Unit) } + // ) + // assertInstanceOf(KeySpec.Mnemonic::class.java, request.keySpec) + + // try { + // request.requestAuthorization().getOrThrow() + // } catch (exception: Exception) { + // assert(false) { "requestAuthorization for Mnemonic should succeed but didn't" } + // } + + // val failingRequest = KeystoreAccessRequest.ForMnemonic( + // onRequestAuthorization = { Result.failure(RuntimeException("An error")) } + // ) + + // try { + // failingRequest.requestAuthorization().getOrThrow() + // assert(false) { "requestAuthorization for failing access to Mnemonic should fail but succeeded" } + // } catch (exception: Exception) { + // assert(true) { "requestAuthorization for failing access to Mnemonic should fail" } + // } + // } } \ No newline at end of file From 2647f7244cfdb3e7068585facc5c3619f701ff6d Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 15:37:46 +0100 Subject: [PATCH 37/70] Fixed??? the MethodTooLargeException by using Arc ? Ive commented out many methods though --- .../profile/mfa/security_structures/mod.rs | 2 +- .../security_shield_builder.rs | 1273 +++++++++-------- 2 files changed, 639 insertions(+), 636 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index 3a43df05b..d1c312969 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -1,7 +1,7 @@ mod matrices; mod models; mod roles; -// mod security_shield_builder; +mod security_shield_builder; mod security_shield_builder_invalid_reason; mod security_structure_id; mod security_structure_metadata; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 65652137b..9ddf1ec47 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -1,635 +1,638 @@ -// #![allow(clippy::new_without_default)] -// #![allow(dead_code)] -// #![allow(unused_variables)] - -// use std::{ -// borrow::Borrow, -// sync::{Arc, RwLock}, -// }; - -// use sargon::{IndexSet, MatrixBuilder}; - -// use crate::prelude::*; - -// /// A builder of `SecurityStructureOfFactorSourceIds` a.k.a. `SecurityShield`, -// /// which contains a MatrixOfFactorSourceIds - with primary, recovery, and -// /// confirmation roles. -// #[derive(Debug, uniffi::Object)] -// pub struct SecurityShieldBuilder { -// wrapped: RwLock, -// } - -// #[uniffi::export] -// impl SecurityShieldBuilder { -// #[uniffi::constructor] -// pub fn new() -> Arc { -// Arc::new(Self { -// wrapped: RwLock::new(sargon::SecurityShieldBuilder::new()), -// }) -// } -// } - -// impl SecurityShieldBuilder { -// fn get( -// &self, -// access: impl Fn(&sargon::SecurityShieldBuilder) -> R, -// ) -> R { -// let binding = self.wrapped.read().unwrap(); -// access(&binding) -// } - -// fn set( -// &self, -// mut write: impl FnMut( -// &mut sargon::SecurityShieldBuilder, -// ) -> &sargon::SecurityShieldBuilder, -// ) { -// let mut binding = self.wrapped.write().expect("No poison"); -// _ = write(&mut binding); -// } - -// fn validation_for_addition_of_factor_source_by_calling( -// &self, -// factor_sources: Vec, -// call: impl Fn( -// &sargon::SecurityShieldBuilder, -// Vec, -// ) -// -> Vec, -// ) -> Vec> { -// let input = factor_sources -// .clone() -// .into_iter() -// .map(Into::::into) -// .collect_vec(); - -// self.get(|builder| { -// call(builder, input.clone()) -// .into_iter() -// .map(Into::::into) -// .map(Arc::new) -// .collect() -// }) -// } -// } - -// impl SecurityShieldBuilder { -// fn get_factors( -// &self, -// access: impl Fn( -// &sargon::SecurityShieldBuilder, -// ) -> Vec, -// ) -> Vec { -// self.get(|builder| { -// let factors = access(builder); -// factors -// .into_iter() -// .map(crate::FactorSourceID::from) -// .collect::>() -// }) -// } -// } - -// // ==================== -// // ==== GET / READ ==== -// // ==================== -// #[uniffi::export] -// impl SecurityShieldBuilder { -// pub fn get_primary_threshold(&self) -> u8 { -// self.get(|builder| builder.get_threshold()) -// } - -// pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { -// self.get(|builder| builder.get_number_of_days_until_auto_confirm()) -// } - -// pub fn get_name(&self) -> String { -// self.get(|builder| builder.get_name()) -// } - -// pub fn get_primary_threshold_factors(&self) -> Vec { -// self.get_factors(|builder| builder.get_primary_threshold_factors()) -// } - -// pub fn get_primary_override_factors(&self) -> Vec { -// self.get_factors(|builder| builder.get_primary_override_factors()) -// } - -// pub fn get_recovery_factors(&self) -> Vec { -// self.get_factors(|builder| builder.get_recovery_factors()) -// } - -// pub fn get_confirmation_factors(&self) -> Vec { -// self.get_factors(|builder| builder.get_confirmation_factors()) -// } -// } - -// // ==================== -// // ===== MUTATION ===== -// // ==================== -// #[uniffi::export] -// impl SecurityShieldBuilder { -// pub fn set_name(&self, name: String) { -// self.set(|builder| builder.set_name(&name)); -// } - -// pub fn remove_factor_from_all_roles( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder -// .remove_factor_from_all_roles(factor_source_id.clone().into()) -// }) -// } - -// pub fn remove_factor_from_primary(&self, factor_source_id: FactorSourceID) { -// self.set(|builder| { -// builder.remove_factor_from_primary(factor_source_id.clone().into()) -// }) -// } - -// pub fn remove_factor_from_recovery( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder.remove_factor_from_recovery(factor_source_id.clone().into()) -// }) -// } - -// pub fn remove_factor_from_confirmation( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder.remove_factor_from_confirmation( -// factor_source_id.clone().into(), -// ) -// }) -// } - -// pub fn set_threshold(&self, threshold: u8) { -// self.set(|builder| builder.set_threshold(threshold)) -// } - -// pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { -// self.set(|builder| { -// builder.set_number_of_days_until_auto_confirm(number_of_days) -// }) -// } - -// /// Adds the factor source to the primary role threshold list. -// pub fn add_factor_source_to_primary_threshold( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder.add_factor_source_to_primary_threshold( -// factor_source_id.clone().into(), -// ) -// }) -// } - -// pub fn add_factor_source_to_primary_override( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder.add_factor_source_to_primary_override( -// factor_source_id.clone().into(), -// ) -// }) -// } - -// pub fn add_factor_source_to_recovery_override( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder.add_factor_source_to_recovery_override( -// factor_source_id.clone().into(), -// ) -// }) -// } - -// pub fn add_factor_source_to_confirmation_override( -// &self, -// factor_source_id: FactorSourceID, -// ) { -// self.set(|builder| { -// builder.add_factor_source_to_confirmation_override( -// factor_source_id.clone().into(), -// ) -// }) -// } -// } - -// #[uniffi::export] -// impl SecurityShieldBuilder { -// pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( -// factor_source_kind.clone().into(), -// ) -// ) -// } - -// pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( -// factor_source_kind.clone().into(), -// ) -// ) -// } - -// pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( -// factor_source_kind.clone().into(), -// ) -// ) -// } - -// pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( -// factor_source_kind.clone().into(), -// ) -// ) -// } - -// pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( -// factor_source_kind.clone().into(), -// ) -// ) -// } - -// pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| { -// builder -// .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( -// factor_source_kind.clone().into(), -// ) -// }) -// } - -// pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( -// factor_source_kind.clone().into(), -// ) -// ) -// } - -// pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( -// &self, -// factor_source_kind: FactorSourceKind, -// ) -> bool { -// self.get(|builder| -// builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( -// factor_source_kind.clone().into(), -// ) -// ) -// } -// } - -// #[uniffi::export] -// impl SecurityShieldBuilder { -// pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( -// &self, -// factor_sources: Vec, -// ) -> Vec> { -// self.validation_for_addition_of_factor_source_by_calling( -// factor_sources, -// |builder, input| { -// builder -// .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) -// }, -// ) -// } - -// pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( -// &self, -// factor_sources: Vec, -// ) -> Vec> { -// self.validation_for_addition_of_factor_source_by_calling( -// factor_sources, -// |builder, input| { -// builder.validation_for_addition_of_factor_source_to_primary_override_for_each(input) -// }, -// ) -// } - -// pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( -// &self, -// factor_sources: Vec, -// ) -> Vec> { -// self.validation_for_addition_of_factor_source_by_calling( -// factor_sources, -// |builder, input| { -// builder -// .validation_for_addition_of_factor_source_to_recovery_override_for_each(input) -// }, -// ) -// } - -// pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( -// &self, -// factor_sources: Vec, -// ) -> Vec> { -// self.validation_for_addition_of_factor_source_by_calling( -// factor_sources, -// |builder, input| { -// builder.validation_for_addition_of_factor_source_to_confirmation_override_for_each( -// input, -// ) -// }, -// ) -// } -// } - -// #[uniffi::export] -// impl SecurityShieldBuilder { -// pub fn validate(&self) -> Option { -// self.get(|builder| builder.validate().map(|x| x.into())) -// } - -// pub fn build( -// &self, -// ) -> Result< -// SecurityStructureOfFactorSourceIDs, -// SecurityShieldBuilderInvalidReason, -// > { -// self.get(|builder| builder.build()) -// .map(|shield| shield.into()) -// .map_err(|x| x.into()) -// } -// } - -// impl FactorSourceID { -// pub fn new(inner: impl Borrow) -> Self { -// Self::from(*inner.borrow()) -// } -// } - -// #[cfg(test)] -// impl FactorSourceID { -// pub fn sample_device() -> Self { -// Self::new(sargon::FactorSourceID::sample_device()) -// } - -// pub fn sample_device_other() -> Self { -// Self::new(sargon::FactorSourceID::sample_device_other()) -// } - -// pub fn sample_ledger() -> Self { -// Self::new(sargon::FactorSourceID::sample_ledger()) -// } - -// pub fn sample_ledger_other() -> Self { -// Self::new(sargon::FactorSourceID::sample_ledger_other()) -// } - -// pub fn sample_arculus() -> Self { -// Self::new(sargon::FactorSourceID::sample_arculus()) -// } - -// pub fn sample_arculus_other() -> Self { -// Self::new(sargon::FactorSourceID::sample_arculus_other()) -// } -// } - -// #[cfg(test)] -// mod tests { - -// use super::*; - -// #[allow(clippy::upper_case_acronyms)] -// type SUT = SecurityShieldBuilder; - -// #[test] -// fn test() { -// let sut = SUT::new(); - -// assert_eq!(sut.get_name(), "My Shield"); -// sut.set_name("S.H.I.E.L.D.".to_owned()); - -// assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); -// sut.set_number_of_days_until_auto_confirm(u16::MAX); -// assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); -// // Primary -// let sim_prim = -// sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ -// FactorSourceID::sample_arculus(), -// ]); - -// let sim_prim_threshold = sut -// .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ -// FactorSourceID::sample_arculus(), -// ]); - -// let sim_kind_prim = sut -// .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( -// FactorSourceKind::Device, -// ); - -// let sim_kind_prim_threshold = sut -// .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( -// FactorSourceKind::Device, -// ); - -// sut.add_factor_source_to_primary_threshold( -// FactorSourceID::sample_device(), -// ); -// assert_eq!( -// sut.get_primary_threshold_factors(), -// vec![FactorSourceID::sample_device()] -// ); -// sut.set_threshold(1); -// assert_eq!(sut.get_primary_threshold(), 1); -// sut.add_factor_source_to_primary_override( -// FactorSourceID::sample_arculus(), -// ); -// sut.add_factor_source_to_primary_override( -// FactorSourceID::sample_arculus_other(), -// ); - -// assert_eq!( -// sut.get_primary_override_factors(), -// vec![ -// FactorSourceID::sample_arculus(), -// FactorSourceID::sample_arculus_other() -// ] -// ); - -// // Recovery -// let sim_rec = -// sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ -// FactorSourceID::sample_ledger(), -// ]); - -// let sim_kind_rec = sut -// .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( -// FactorSourceKind::ArculusCard, -// ); - -// sut.add_factor_source_to_recovery_override( -// FactorSourceID::sample_ledger(), -// ); -// sut.add_factor_source_to_recovery_override( -// FactorSourceID::sample_ledger_other(), -// ); - -// assert_eq!( -// sut.get_recovery_factors(), -// vec![ -// FactorSourceID::sample_ledger(), -// FactorSourceID::sample_ledger_other() -// ] -// ); - -// // Confirmation -// let sim_conf = sut -// .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ -// FactorSourceID::sample_device(), -// ]); - -// let sim_kind_conf = sut -// .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( -// FactorSourceKind::ArculusCard, -// ); - -// sut.add_factor_source_to_confirmation_override( -// FactorSourceID::sample_device(), -// ); - -// assert_eq!( -// sut.get_confirmation_factors(), -// vec![FactorSourceID::sample_device(),] -// ); -// /* - -// Reintroduce these asserts when we reintroduce the field `reason_if_invalid` inside `FactorSourceValidationStatus` - -// assert_ne!( -// sim_prim, -// sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ -// FactorSourceID::sample_arculus(), -// ]) -// ); - -// assert_ne!( -// sim_prim_threshold, -// sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ -// FactorSourceID::sample_arculus() -// ]) -// ); - -// assert_ne!( -// sim_rec, -// sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ -// FactorSourceID::sample_ledger(), -// ]) -// ); - -// assert_ne!( -// sim_conf, -// sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ -// FactorSourceID::sample_device(), -// ]) -// ); - -// assert_ne!( -// sim_kind_prim, -// sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( -// FactorSourceKind::Device, -// ) -// ); - -// assert_ne!( -// sim_kind_prim_threshold, -// sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( -// FactorSourceKind::Device, -// ) -// ); - -// assert_eq!( -// sim_kind_rec, -// sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( -// FactorSourceKind::ArculusCard, -// ) -// ); - -// assert_eq!( -// sim_kind_conf, -// sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( -// FactorSourceKind::ArculusCard, -// ) -// ); -// */ -// sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); -// sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); - -// let f = FactorSourceID::sample_ledger_other(); -// let xs = sut.get_primary_override_factors(); -// sut.add_factor_source_to_primary_override(f.clone()); -// sut.remove_factor_from_primary(f.clone()); -// assert_eq!(xs, sut.get_primary_override_factors()); - -// let xs = sut.get_recovery_factors(); -// sut.add_factor_source_to_recovery_override(f.clone()); -// sut.remove_factor_from_recovery(f.clone()); -// assert_eq!(xs, sut.get_recovery_factors()); - -// let xs = sut.get_confirmation_factors(); -// sut.add_factor_source_to_confirmation_override(f.clone()); -// sut.remove_factor_from_confirmation(f.clone()); -// assert_eq!(xs, sut.get_confirmation_factors()); - -// let v0 = sut.validate(); -// let v1 = sut.validate(); // can call validate many times! -// assert_eq!(v0, v1); - -// let shield0 = sut.build().unwrap(); -// let shield = sut.build().unwrap(); // can call build many times! -// assert_eq!(shield0, shield); - -// assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); -// assert_eq!( -// shield.matrix_of_factors.primary_role.override_factors, -// vec![FactorSourceID::sample_arculus()] -// ); -// assert_eq!( -// shield.matrix_of_factors.recovery_role.override_factors, -// vec![FactorSourceID::sample_ledger()] -// ); -// assert_eq!( -// shield.matrix_of_factors.confirmation_role.override_factors, -// vec![FactorSourceID::sample_device()] -// ); -// } -// } +#![allow(clippy::new_without_default)] +#![allow(dead_code)] +#![allow(unused_variables)] + +use std::{ + borrow::Borrow, + sync::{Arc, RwLock}, +}; + +use sargon::{IndexSet, MatrixBuilder}; + +use crate::prelude::*; + +/// A builder of `SecurityStructureOfFactorSourceIds` a.k.a. `SecurityShield`, +/// which contains a MatrixOfFactorSourceIds - with primary, recovery, and +/// confirmation roles. +#[derive(Debug, uniffi::Object)] +pub struct SecurityShieldBuilder { + wrapped: RwLock, +} + +#[uniffi::export] +impl SecurityShieldBuilder { + #[uniffi::constructor] + pub fn new() -> Arc { + Arc::new(Self { + wrapped: RwLock::new(sargon::SecurityShieldBuilder::new()), + }) + } +} + +impl SecurityShieldBuilder { + fn get( + self: Arc, + access: impl Fn(&sargon::SecurityShieldBuilder) -> R, + ) -> R { + let binding = self.wrapped.read().unwrap(); + access(&binding) + } + + fn set( + self: Arc, + mut write: impl FnMut( + &mut sargon::SecurityShieldBuilder, + ) -> &sargon::SecurityShieldBuilder, + ) { + let mut binding = self.wrapped.write().expect("No poison"); + _ = write(&mut binding); + } + + fn validation_for_addition_of_factor_source_by_calling( + self: Arc, + factor_sources: Vec, + call: impl Fn( + &sargon::SecurityShieldBuilder, + Vec, + ) + -> Vec, + ) -> Vec> { + let input = factor_sources + .clone() + .into_iter() + .map(Into::::into) + .collect_vec(); + + self.get(|builder| { + call(builder, input.clone()) + .into_iter() + .map(Into::::into) + .map(Arc::new) + .collect() + }) + } +} + +impl SecurityShieldBuilder { + fn get_factors( + self: Arc, + access: impl Fn( + &sargon::SecurityShieldBuilder, + ) -> Vec, + ) -> Vec { + self.get(|builder| { + let factors = access(builder); + factors + .into_iter() + .map(crate::FactorSourceID::from) + .collect::>() + }) + } +} +// ==================== +// ==== GET / READ ==== +// ==================== +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn get_primary_threshold(self: Arc) -> u8 { + self.get(|builder| builder.get_threshold()) + } +} + +/* + pub fn get_number_of_days_until_auto_confirm(self: Arc) -> u16 { + self.get(|builder| builder.get_number_of_days_until_auto_confirm()) + } + + pub fn get_name(self: Arc) -> String { + self.get(|builder| builder.get_name()) + } + + pub fn get_primary_threshold_factors(self: Arc) -> Vec { + self.get_factors(|builder| builder.get_primary_threshold_factors()) + } + + pub fn get_primary_override_factors(self: Arc) -> Vec { + self.get_factors(|builder| builder.get_primary_override_factors()) + } + + pub fn get_recovery_factors(self: Arc) -> Vec { + self.get_factors(|builder| builder.get_recovery_factors()) + } + + pub fn get_confirmation_factors(self: Arc) -> Vec { + self.get_factors(|builder| builder.get_confirmation_factors()) + } +} + +// ==================== +// ===== MUTATION ===== +// ==================== +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn set_name(self: Arc, name: String) { + self.set(|builder| builder.set_name(&name)); + } + + pub fn remove_factor_from_all_roles( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder + .remove_factor_from_all_roles(factor_source_id.clone().into()) + }) + } + + pub fn remove_factor_from_primary(self: Arc, factor_source_id: FactorSourceID) { + self.set(|builder| { + builder.remove_factor_from_primary(factor_source_id.clone().into()) + }) + } + + pub fn remove_factor_from_recovery( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.remove_factor_from_recovery(factor_source_id.clone().into()) + }) + } + + pub fn remove_factor_from_confirmation( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.remove_factor_from_confirmation( + factor_source_id.clone().into(), + ) + }) + } + + pub fn set_threshold(self: Arc, threshold: u8) { + self.set(|builder| builder.set_threshold(threshold)) + } + + pub fn set_number_of_days_until_auto_confirm(self: Arc, number_of_days: u16) { + self.set(|builder| { + builder.set_number_of_days_until_auto_confirm(number_of_days) + }) + } + + /// Adds the factor source to the primary role threshold list. + pub fn add_factor_source_to_primary_threshold( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.add_factor_source_to_primary_threshold( + factor_source_id.clone().into(), + ) + }) + } + + pub fn add_factor_source_to_primary_override( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.add_factor_source_to_primary_override( + factor_source_id.clone().into(), + ) + }) + } + + pub fn add_factor_source_to_recovery_override( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.add_factor_source_to_recovery_override( + factor_source_id.clone().into(), + ) + }) + } + + pub fn add_factor_source_to_confirmation_override( + self: Arc, + factor_source_id: FactorSourceID, + ) { + self.set(|builder| { + builder.add_factor_source_to_confirmation_override( + factor_source_id.clone().into(), + ) + }) + } +} + +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| { + builder + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + factor_source_kind.clone().into(), + ) + }) + } + + pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( + factor_source_kind.clone().into(), + ) + ) + } + + pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + self: Arc, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.get(|builder| + builder.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + factor_source_kind.clone().into(), + ) + ) + } +} + +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( + self: Arc, + factor_sources: Vec, + ) -> Vec> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder + .validation_for_addition_of_factor_source_to_primary_threshold_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( + self: Arc, + factor_sources: Vec, + ) -> Vec> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder.validation_for_addition_of_factor_source_to_primary_override_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( + self: Arc, + factor_sources: Vec, + ) -> Vec> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder + .validation_for_addition_of_factor_source_to_recovery_override_for_each(input) + }, + ) + } + + pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( + self: Arc, + factor_sources: Vec, + ) -> Vec> { + self.validation_for_addition_of_factor_source_by_calling( + factor_sources, + |builder, input| { + builder.validation_for_addition_of_factor_source_to_confirmation_override_for_each( + input, + ) + }, + ) + } +} + +#[uniffi::export] +impl SecurityShieldBuilder { + pub fn validate(self: Arc) -> Option { + self.get(|builder| builder.validate().map(|x| x.into())) + } + + pub fn build( + self: Arc, + ) -> Result< + SecurityStructureOfFactorSourceIDs, + SecurityShieldBuilderInvalidReason, + > { + self.get(|builder| builder.build()) + .map(|shield| shield.into()) + .map_err(|x| x.into()) + } +} + +impl FactorSourceID { + pub fn new(inner: impl Borrow) -> Self { + Self::from(*inner.borrow()) + } +} + +#[cfg(test)] +impl FactorSourceID { + pub fn sample_device() -> Self { + Self::new(sargon::FactorSourceID::sample_device()) + } + + pub fn sample_device_other() -> Self { + Self::new(sargon::FactorSourceID::sample_device_other()) + } + + pub fn sample_ledger() -> Self { + Self::new(sargon::FactorSourceID::sample_ledger()) + } + + pub fn sample_ledger_other() -> Self { + Self::new(sargon::FactorSourceID::sample_ledger_other()) + } + + pub fn sample_arculus() -> Self { + Self::new(sargon::FactorSourceID::sample_arculus()) + } + + pub fn sample_arculus_other() -> Self { + Self::new(sargon::FactorSourceID::sample_arculus_other()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = SecurityShieldBuilder; + + #[test] + fn test() { + let sut = SUT::new(); + + assert_eq!(sut.get_name(), "My Shield"); + sut.set_name("S.H.I.E.L.D.".to_owned()); + + assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); + sut.set_number_of_days_until_auto_confirm(u16::MAX); + assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); + // Primary + let sim_prim = + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]); + + let sim_prim_threshold = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus(), + ]); + + let sim_kind_prim = sut + .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + FactorSourceKind::Device, + ); + + let sim_kind_prim_threshold = sut + .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + FactorSourceKind::Device, + ); + + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ); + assert_eq!( + sut.get_primary_threshold_factors(), + vec![FactorSourceID::sample_device()] + ); + sut.set_threshold(1); + assert_eq!(sut.get_primary_threshold(), 1); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_arculus(), + ); + sut.add_factor_source_to_primary_override( + FactorSourceID::sample_arculus_other(), + ); + + assert_eq!( + sut.get_primary_override_factors(), + vec![ + FactorSourceID::sample_arculus(), + FactorSourceID::sample_arculus_other() + ] + ); + + // Recovery + let sim_rec = + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]); + + let sim_kind_rec = sut + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::ArculusCard, + ); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ); + + assert_eq!( + sut.get_recovery_factors(), + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other() + ] + ); + + // Confirmation + let sim_conf = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]); + + let sim_kind_conf = sut + .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::ArculusCard, + ); + + sut.add_factor_source_to_confirmation_override( + FactorSourceID::sample_device(), + ); + + assert_eq!( + sut.get_confirmation_factors(), + vec![FactorSourceID::sample_device(),] + ); + /* + + Reintroduce these asserts when we reintroduce the field `reason_if_invalid` inside `FactorSourceValidationStatus` + + assert_ne!( + sim_prim, + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]) + ); + + assert_ne!( + sim_prim_threshold, + sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus() + ]) + ); + + assert_ne!( + sim_rec, + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]) + ); + + assert_ne!( + sim_conf, + sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]) + ); + + assert_ne!( + sim_kind_prim, + sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + FactorSourceKind::Device, + ) + ); + + assert_ne!( + sim_kind_prim_threshold, + sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + FactorSourceKind::Device, + ) + ); + + assert_eq!( + sim_kind_rec, + sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::ArculusCard, + ) + ); + + assert_eq!( + sim_kind_conf, + sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::ArculusCard, + ) + ); + */ + sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); + sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); + + let f = FactorSourceID::sample_ledger_other(); + let xs = sut.get_primary_override_factors(); + sut.add_factor_source_to_primary_override(f.clone()); + sut.remove_factor_from_primary(f.clone()); + assert_eq!(xs, sut.get_primary_override_factors()); + + let xs = sut.get_recovery_factors(); + sut.add_factor_source_to_recovery_override(f.clone()); + sut.remove_factor_from_recovery(f.clone()); + assert_eq!(xs, sut.get_recovery_factors()); + + let xs = sut.get_confirmation_factors(); + sut.add_factor_source_to_confirmation_override(f.clone()); + sut.remove_factor_from_confirmation(f.clone()); + assert_eq!(xs, sut.get_confirmation_factors()); + + let v0 = sut.validate(); + let v1 = sut.validate(); // can call validate many times! + assert_eq!(v0, v1); + + let shield0 = sut.build().unwrap(); + let shield = sut.build().unwrap(); // can call build many times! + assert_eq!(shield0, shield); + + assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); + assert_eq!( + shield.matrix_of_factors.primary_role.override_factors, + vec![FactorSourceID::sample_arculus()] + ); + assert_eq!( + shield.matrix_of_factors.recovery_role.override_factors, + vec![FactorSourceID::sample_ledger()] + ); + assert_eq!( + shield.matrix_of_factors.confirmation_role.override_factors, + vec![FactorSourceID::sample_device()] + ); + } +} + +*/ From 146f392b278f6c97f171f5ec7d7e6fa7f0474a63 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 15:53:19 +0100 Subject: [PATCH 38/70] Prevented MethodTooLargeException still? add more methods... is the `validation_for_addition_of_factor_source_to_confirmation_override_for_each` problematic? --- .../security_shield_builder.rs | 215 ++++++++++-------- 1 file changed, 117 insertions(+), 98 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 9ddf1ec47..191594e4d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -97,9 +97,7 @@ impl SecurityShieldBuilder { pub fn get_primary_threshold(self: Arc) -> u8 { self.get(|builder| builder.get_threshold()) } -} -/* pub fn get_number_of_days_until_auto_confirm(self: Arc) -> u16 { self.get(|builder| builder.get_number_of_days_until_auto_confirm()) } @@ -108,11 +106,15 @@ impl SecurityShieldBuilder { self.get(|builder| builder.get_name()) } - pub fn get_primary_threshold_factors(self: Arc) -> Vec { + pub fn get_primary_threshold_factors( + self: Arc, + ) -> Vec { self.get_factors(|builder| builder.get_primary_threshold_factors()) } - pub fn get_primary_override_factors(self: Arc) -> Vec { + pub fn get_primary_override_factors( + self: Arc, + ) -> Vec { self.get_factors(|builder| builder.get_primary_override_factors()) } @@ -144,7 +146,10 @@ impl SecurityShieldBuilder { }) } - pub fn remove_factor_from_primary(self: Arc, factor_source_id: FactorSourceID) { + pub fn remove_factor_from_primary( + self: Arc, + factor_source_id: FactorSourceID, + ) { self.set(|builder| { builder.remove_factor_from_primary(factor_source_id.clone().into()) }) @@ -174,7 +179,10 @@ impl SecurityShieldBuilder { self.set(|builder| builder.set_threshold(threshold)) } - pub fn set_number_of_days_until_auto_confirm(self: Arc, number_of_days: u16) { + pub fn set_number_of_days_until_auto_confirm( + self: Arc, + number_of_days: u16, + ) { self.set(|builder| { builder.set_number_of_days_until_auto_confirm(number_of_days) }) @@ -317,7 +325,7 @@ impl SecurityShieldBuilder { ) } } - +/* #[uniffi::export] impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( @@ -372,13 +380,17 @@ impl SecurityShieldBuilder { ) } } - +*/ #[uniffi::export] impl SecurityShieldBuilder { - pub fn validate(self: Arc) -> Option { + pub fn validate( + self: Arc, + ) -> Option { self.get(|builder| builder.validate().map(|x| x.into())) } - +} +impl SecurityShieldBuilder { + /// TODO: SHOULD `uniffi::export` this!!!!! pub fn build( self: Arc, ) -> Result< @@ -436,51 +448,54 @@ mod tests { fn test() { let sut = SUT::new(); - assert_eq!(sut.get_name(), "My Shield"); - sut.set_name("S.H.I.E.L.D.".to_owned()); + assert_eq!(sut.clone().get_name(), "My Shield"); + sut.clone().set_name("S.H.I.E.L.D.".to_owned()); - assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); - sut.set_number_of_days_until_auto_confirm(u16::MAX); - assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); + assert_eq!(sut.clone().get_number_of_days_until_auto_confirm(), 14); + sut.clone().set_number_of_days_until_auto_confirm(u16::MAX); + assert_eq!( + sut.clone().get_number_of_days_until_auto_confirm(), + u16::MAX + ); // Primary - let sim_prim = - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - FactorSourceID::sample_arculus(), - ]); - - let sim_prim_threshold = sut - .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - FactorSourceID::sample_arculus(), - ]); - - let sim_kind_prim = sut - .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - FactorSourceKind::Device, - ); - - let sim_kind_prim_threshold = sut - .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - FactorSourceKind::Device, - ); - - sut.add_factor_source_to_primary_threshold( + // let sim_prim = + // sut.clone().validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + // FactorSourceID::sample_arculus(), + // ]); + + // let sim_prim_threshold = sut.clone() + // .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + // FactorSourceID::sample_arculus(), + // ]); + + // let sim_kind_prim = sut.clone() + // .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + // FactorSourceKind::Device, + // ); + + // let sim_kind_prim_threshold = sut.clone() + // .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + // FactorSourceKind::Device, + // ); + + sut.clone().add_factor_source_to_primary_threshold( FactorSourceID::sample_device(), ); assert_eq!( - sut.get_primary_threshold_factors(), + sut.clone().get_primary_threshold_factors(), vec![FactorSourceID::sample_device()] ); - sut.set_threshold(1); - assert_eq!(sut.get_primary_threshold(), 1); - sut.add_factor_source_to_primary_override( + sut.clone().set_threshold(1); + assert_eq!(sut.clone().get_primary_threshold(), 1); + sut.clone().add_factor_source_to_primary_override( FactorSourceID::sample_arculus(), ); - sut.add_factor_source_to_primary_override( + sut.clone().add_factor_source_to_primary_override( FactorSourceID::sample_arculus_other(), ); assert_eq!( - sut.get_primary_override_factors(), + sut.clone().get_primary_override_factors(), vec![ FactorSourceID::sample_arculus(), FactorSourceID::sample_arculus_other() @@ -488,25 +503,26 @@ mod tests { ); // Recovery - let sim_rec = - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - FactorSourceID::sample_ledger(), - ]); - - let sim_kind_rec = sut - .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - FactorSourceKind::ArculusCard, - ); - - sut.add_factor_source_to_recovery_override( + // let sim_rec = + // sut.clone().validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + // FactorSourceID::sample_ledger(), + // ]); + + // let sim_kind_rec = sut + // .clone() + // .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + // FactorSourceKind::ArculusCard, + // ); + + sut.clone().add_factor_source_to_recovery_override( FactorSourceID::sample_ledger(), ); - sut.add_factor_source_to_recovery_override( + sut.clone().add_factor_source_to_recovery_override( FactorSourceID::sample_ledger_other(), ); assert_eq!( - sut.get_recovery_factors(), + sut.clone().get_recovery_factors(), vec![ FactorSourceID::sample_ledger(), FactorSourceID::sample_ledger_other() @@ -514,22 +530,23 @@ mod tests { ); // Confirmation - let sim_conf = sut - .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - FactorSourceID::sample_device(), - ]); - - let sim_kind_conf = sut - .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - FactorSourceKind::ArculusCard, - ); - - sut.add_factor_source_to_confirmation_override( + // let sim_conf = sut.clone() + // .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + // FactorSourceID::sample_device(), + // ]); + + // let sim_kind_conf = sut + // .clone() + // .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + // FactorSourceKind::ArculusCard, + // ); + + sut.clone().add_factor_source_to_confirmation_override( FactorSourceID::sample_device(), ); assert_eq!( - sut.get_confirmation_factors(), + sut.clone().get_confirmation_factors(), vec![FactorSourceID::sample_device(),] ); /* @@ -538,85 +555,89 @@ mod tests { assert_ne!( sim_prim, - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + sut.clone().validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ FactorSourceID::sample_arculus(), ]) ); assert_ne!( sim_prim_threshold, - sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + sut.clone().validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ FactorSourceID::sample_arculus() ]) ); assert_ne!( sim_rec, - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + sut.clone().validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ FactorSourceID::sample_ledger(), ]) ); assert_ne!( sim_conf, - sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + sut.clone().validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ FactorSourceID::sample_device(), ]) ); assert_ne!( sim_kind_prim, - sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + sut.clone().addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( FactorSourceKind::Device, ) ); assert_ne!( sim_kind_prim_threshold, - sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + sut.clone().addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( FactorSourceKind::Device, ) ); assert_eq!( sim_kind_rec, - sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + sut.clone().addition_of_factor_source_of_kind_to_recovery_is_fully_valid( FactorSourceKind::ArculusCard, ) ); assert_eq!( sim_kind_conf, - sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + sut.clone().addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( FactorSourceKind::ArculusCard, ) ); */ - sut.remove_factor_from_all_roles(FactorSourceID::sample_arculus_other()); - sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); + sut.clone().remove_factor_from_all_roles( + FactorSourceID::sample_arculus_other(), + ); + sut.clone().remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); let f = FactorSourceID::sample_ledger_other(); - let xs = sut.get_primary_override_factors(); - sut.add_factor_source_to_primary_override(f.clone()); - sut.remove_factor_from_primary(f.clone()); - assert_eq!(xs, sut.get_primary_override_factors()); - - let xs = sut.get_recovery_factors(); - sut.add_factor_source_to_recovery_override(f.clone()); - sut.remove_factor_from_recovery(f.clone()); - assert_eq!(xs, sut.get_recovery_factors()); - - let xs = sut.get_confirmation_factors(); - sut.add_factor_source_to_confirmation_override(f.clone()); - sut.remove_factor_from_confirmation(f.clone()); - assert_eq!(xs, sut.get_confirmation_factors()); - - let v0 = sut.validate(); - let v1 = sut.validate(); // can call validate many times! + let xs = sut.clone().get_primary_override_factors(); + sut.clone().add_factor_source_to_primary_override(f.clone()); + sut.clone().remove_factor_from_primary(f.clone()); + assert_eq!(xs, sut.clone().get_primary_override_factors()); + + let xs = sut.clone().get_recovery_factors(); + sut.clone() + .add_factor_source_to_recovery_override(f.clone()); + sut.clone().remove_factor_from_recovery(f.clone()); + assert_eq!(xs, sut.clone().get_recovery_factors()); + + let xs = sut.clone().get_confirmation_factors(); + sut.clone() + .add_factor_source_to_confirmation_override(f.clone()); + sut.clone().remove_factor_from_confirmation(f.clone()); + assert_eq!(xs, sut.clone().get_confirmation_factors()); + + let v0 = sut.clone().validate(); + let v1 = sut.clone().validate(); // can call validate many times! assert_eq!(v0, v1); - let shield0 = sut.build().unwrap(); - let shield = sut.build().unwrap(); // can call build many times! + let shield0 = sut.clone().build().unwrap(); + let shield = sut.clone().build().unwrap(); // can call build many times! assert_eq!(shield0, shield); assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); @@ -634,5 +655,3 @@ mod tests { ); } } - -*/ From 3ecd7950b189c537567a72d3f89fea809c2fcaa1 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 16:12:08 +0100 Subject: [PATCH 39/70] Still working? MethodTooLargeException....(kotlin) trying to fix swift --- .../MFA/SecurityShieldBuilder+Swifified.swift | 26 +++++++------- .../MFA/SecurityShieldsBuilderTests.swift | 34 +++++++++---------- .../security_shield_builder.rs | 1 + 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift index 2bd1aaff6..b2d7a489f 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift @@ -1,18 +1,18 @@ import SargonUniFFI -extension FactorSourceValidationStatus { - public var factorSourceID: FactorSourceID { - factorSourceId() - } - -// public var reasonIfInvalid: SecurityShieldBuilderInvalidReason? { -// self.reasonIfInvalid() +//extension FactorSourceValidationStatus { +// public var factorSourceID: FactorSourceID { +// factorSourceId() // } - - public var role: RoleKind { - role() - } -} +// +//// public var reasonIfInvalid: SecurityShieldBuilderInvalidReason? { +//// self.reasonIfInvalid() +//// } +// +// public var role: RoleKind { +// role() +// } +//} extension SecurityShieldBuilder { public typealias Factor = FactorSourceID @@ -22,7 +22,7 @@ extension SecurityShieldBuilder { get { getNumberOfDaysUntilAutoConfirm() } set { precondition(newValue > 0, "Number of days until auto confirm must be greater than zero.") - try! setNumberOfDaysUntilAutoConfirm(numberOfDays: UInt16(newValue)) + setNumberOfDaysUntilAutoConfirm(numberOfDays: UInt16(newValue)) } } diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index 92073250e..0d5d3a0c1 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -14,23 +14,23 @@ struct ShieldTests { #expect(builder.name == "S.H.I.E.L.D") } - @Test("threshold") - func threshold() { - let builder = SecurityShieldBuilder() - #expect(builder.threshold == 0) - #expect(throws: CommonError.RoleMustHaveAtLeastOneFactor) { - try builder.setThreshold(threshold: 0) - } - } - - @Test("days") - func days() { - let builder = SecurityShieldBuilder() - #expect(builder.numberOfDaysUntilAutoConfirm == 14) - #expect(throws: CommonError.NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) { - try builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 0) - } - } +// @Test("threshold") +// func threshold() { +// let builder = SecurityShieldBuilder() +// #expect(builder.threshold == 0) +// #expect(throws: CommonError.RoleMustHaveAtLeastOneFactor) { +// try builder.setThreshold(threshold: 0) +// } +// } +// +// @Test("days") +// func days() { +// let builder = SecurityShieldBuilder() +// #expect(builder.numberOfDaysUntilAutoConfirm == 14) +// #expect(throws: CommonError.NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) { +// try builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 0) +// } +// } @Test("empty primary threshold") func emptyThresholdFactors() { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 191594e4d..4270ccc58 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -380,6 +380,7 @@ impl SecurityShieldBuilder { ) } } + */ #[uniffi::export] impl SecurityShieldBuilder { From a75bdf5cafafccc032cfad94fdd10d2da258bdcd Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 16:12:57 +0100 Subject: [PATCH 40/70] Still working? MethodTooLargeException....(kotlin) trying to fix swift --- .../profile/mfa/security_structures/security_shield_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 4270ccc58..facac4a91 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -380,7 +380,7 @@ impl SecurityShieldBuilder { ) } } - + */ #[uniffi::export] impl SecurityShieldBuilder { From 55056ef342f4ca707efd9218061b3028e8f1a77a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 16:14:35 +0100 Subject: [PATCH 41/70] Still working? MethodTooLargeException....(kotlin) trying to fix swift (swiftformat) --- .../Profile/MFA/SecurityShieldBuilder+Swifified.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift index b2d7a489f..47ccc2fd9 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift @@ -1,6 +1,6 @@ import SargonUniFFI -//extension FactorSourceValidationStatus { +// extension FactorSourceValidationStatus { // public var factorSourceID: FactorSourceID { // factorSourceId() // } @@ -12,7 +12,7 @@ import SargonUniFFI // public var role: RoleKind { // role() // } -//} +// } extension SecurityShieldBuilder { public typealias Factor = FactorSourceID From 331982df516e6dffc3582a7deb9611bbeaca3133 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 16:48:08 +0100 Subject: [PATCH 42/70] still valid? MethodTooLargeException not triggered? --- .../mfa/security_structures/security_shield_builder.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index facac4a91..e60217452 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -325,7 +325,7 @@ impl SecurityShieldBuilder { ) } } -/* + #[uniffi::export] impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( @@ -365,7 +365,8 @@ impl SecurityShieldBuilder { }, ) } - +} +/* pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( self: Arc, factor_sources: Vec, From c89f8e0b257b6105af58921dc49f4b58db42374a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 16:57:19 +0100 Subject: [PATCH 43/70] still valid? MethodTooLargeException not triggered? added the last method to SecurityShieldBuilder. Hmm. Lets re add `reason_if_invalid: Option to `FactorSourceValidationStatus` to see if all breaks --- .../profile/mfa/security_structures/security_shield_builder.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index e60217452..2e9ce5ef6 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -365,8 +365,6 @@ impl SecurityShieldBuilder { }, ) } -} -/* pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( self: Arc, factor_sources: Vec, @@ -382,7 +380,6 @@ impl SecurityShieldBuilder { } } -*/ #[uniffi::export] impl SecurityShieldBuilder { pub fn validate( From 64b67bed050d5d9dc691f24a85c39a97427e8ab0 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 18:12:35 +0100 Subject: [PATCH 44/70] still valid? MethodTooLargeException not triggered? readding `reason_if_invalid` field in `FactorSourceValidationStatus` object. Still not being returned by any uniffi exported method. --- ...ource_in_role_builder_validation_status.rs | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index b02520007..2a169ce4f 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -2,18 +2,18 @@ use sargon::AsShieldBuilderViolation; use crate::prelude::*; -#[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Object)] +#[derive(Clone, Debug, PartialEq, uniffi::Object)] pub struct FactorSourceValidationStatus { pub role: RoleKind, pub factor_source_id: FactorSourceID, - // pub reason_if_invalid: Option, + pub reason_if_invalid: Option, } -// #[derive(Clone, Debug, PartialEq, Eq, Hash, uniffi::Enum)] -// pub enum FactorSourceValidationStatusReasonIfInvalid { -// BasicViolation(String), -// NonBasic(SecurityShieldBuilderInvalidReason), -// } +#[derive(Clone, Debug, PartialEq, uniffi::Enum)] +pub enum FactorSourceValidationStatusReasonIfInvalid { + BasicViolation(String), + NonBasic(SecurityShieldBuilderInvalidReason), +} // #[uniffi::export] // impl FactorSourceValidationStatus { @@ -35,36 +35,36 @@ impl From for FactorSourceValidationStatus { fn from(val: sargon::FactorSourceInRoleBuilderValidationStatus) -> Self { - // let reason_if_invalid: Option< - // FactorSourceValidationStatusReasonIfInvalid, - // > = { - // match val.validation { - // Ok(_) => None, - // Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( - // FactorSourceValidationStatusReasonIfInvalid::BasicViolation( - // format!("{:?}", b), - // ), - // ), - // Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v - // .as_shield_validation() - // .map(SecurityShieldBuilderInvalidReason::from) - // .map(|x| { - // FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) - // }), - // Err(sargon::RoleBuilderValidation::NotYetValid(v)) => ( - // val.role, v, - // ) - // .as_shield_validation() - // .map(SecurityShieldBuilderInvalidReason::from) - // .map(|x| { - // FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) - // }), - // } - // }; + let reason_if_invalid: Option< + FactorSourceValidationStatusReasonIfInvalid, + > = { + match val.validation { + Ok(_) => None, + Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( + FactorSourceValidationStatusReasonIfInvalid::BasicViolation( + format!("{:?}", b), + ), + ), + Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v + .as_shield_validation() + .map(SecurityShieldBuilderInvalidReason::from) + .map(|x| { + FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) + }), + Err(sargon::RoleBuilderValidation::NotYetValid(v)) => ( + val.role, v, + ) + .as_shield_validation() + .map(SecurityShieldBuilderInvalidReason::from) + .map(|x| { + FactorSourceValidationStatusReasonIfInvalid::NonBasic(x) + }), + } + }; FactorSourceValidationStatus { role: val.role.into(), factor_source_id: val.factor_source_id.into(), - // reason_if_invalid, + reason_if_invalid, } } } From 5a5a19c391d07af1e4d7f9f42c2d19faece54d35 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 19:39:12 +0100 Subject: [PATCH 45/70] still compiles? WIP MethodTooLargeException --- ...ource_in_role_builder_validation_status.rs | 28 +++++++++---------- .../security_shield_builder_invalid_reason.rs | 12 -------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 2a169ce4f..1d31c29ae 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -15,22 +15,22 @@ pub enum FactorSourceValidationStatusReasonIfInvalid { NonBasic(SecurityShieldBuilderInvalidReason), } -// #[uniffi::export] -// impl FactorSourceValidationStatus { -// pub fn reason_if_invalid( -// &self, -// ) -> Option { -// self.reason_if_invalid.clone() -// } +#[uniffi::export] +impl FactorSourceValidationStatus { + // pub fn reason_if_invalid( + // &self, + // ) -> Option { + // self.reason_if_invalid.clone() + // } -// pub fn role(&self) -> RoleKind { -// self.role -// } + // pub fn role(&self) -> RoleKind { + // self.role + // } -// pub fn factor_source_id(&self) -> FactorSourceID { -// self.factor_source_id.clone() -// } -// } + // pub fn factor_source_id(&self) -> FactorSourceID { + // self.factor_source_id.clone() + // } +} impl From for FactorSourceValidationStatus { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index b1c56d892..c094a3cc2 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -3,18 +3,6 @@ use sargon::SecurityShieldBuilderInvalidReason as InternalSecurityShieldBuilderI use thiserror::Error as ThisError; -// #[derive( -// Clone, -// Copy, -// Debug, -// PartialEq, -// Eq, -// Hash, -// InternalConversion, -// ThisError, -// uniffi::Error, -// )] - #[repr(u32)] #[derive( Clone, Debug, ThisError, PartialEq, InternalConversion, uniffi::Error, From 4abaf862ca3f1623fbf01858693c56616f4a0750 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 20:21:07 +0100 Subject: [PATCH 46/70] still compiles? WIP MethodTooLargeException - change `FactorSourceValidationStatus` from uniffi::Object -> uniffi::Record and drop arcs in vec --- ...ource_in_role_builder_validation_status.rs | 28 ++++--------------- .../security_shield_builder.rs | 11 ++++---- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 1d31c29ae..98da53a1d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -2,7 +2,7 @@ use sargon::AsShieldBuilderViolation; use crate::prelude::*; -#[derive(Clone, Debug, PartialEq, uniffi::Object)] +#[derive(Clone, Debug, PartialEq, uniffi::Record)] pub struct FactorSourceValidationStatus { pub role: RoleKind, pub factor_source_id: FactorSourceID, @@ -11,26 +11,10 @@ pub struct FactorSourceValidationStatus { #[derive(Clone, Debug, PartialEq, uniffi::Enum)] pub enum FactorSourceValidationStatusReasonIfInvalid { - BasicViolation(String), + BasicViolation, NonBasic(SecurityShieldBuilderInvalidReason), } -#[uniffi::export] -impl FactorSourceValidationStatus { - // pub fn reason_if_invalid( - // &self, - // ) -> Option { - // self.reason_if_invalid.clone() - // } - - // pub fn role(&self) -> RoleKind { - // self.role - // } - - // pub fn factor_source_id(&self) -> FactorSourceID { - // self.factor_source_id.clone() - // } -} impl From for FactorSourceValidationStatus { @@ -40,10 +24,10 @@ impl From > = { match val.validation { Ok(_) => None, - Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( - FactorSourceValidationStatusReasonIfInvalid::BasicViolation( - format!("{:?}", b), - ), + Err(sargon::RoleBuilderValidation::BasicViolation(_)) => Some( + FactorSourceValidationStatusReasonIfInvalid::BasicViolation, // FactorSourceValidationStatusReasonIfInvalid::BasicViolation( + // format!("{:?}", b), + // ), ), Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v .as_shield_validation() diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 2e9ce5ef6..b632d4a98 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -56,7 +56,7 @@ impl SecurityShieldBuilder { Vec, ) -> Vec, - ) -> Vec> { + ) -> Vec { let input = factor_sources .clone() .into_iter() @@ -67,7 +67,6 @@ impl SecurityShieldBuilder { call(builder, input.clone()) .into_iter() .map(Into::::into) - .map(Arc::new) .collect() }) } @@ -331,7 +330,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( self: Arc, factor_sources: Vec, - ) -> Vec> { + ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -344,7 +343,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( self: Arc, factor_sources: Vec, - ) -> Vec> { + ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -356,7 +355,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( self: Arc, factor_sources: Vec, - ) -> Vec> { + ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { @@ -368,7 +367,7 @@ impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( self: Arc, factor_sources: Vec, - ) -> Vec> { + ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( factor_sources, |builder, input| { From aac1f8c060000ce00e38438a3932e7baafa3a75b Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 20:47:12 +0100 Subject: [PATCH 47/70] still compiles? WIP MethodTooLargeException: re-introduce build --- .../mfa/security_structures/security_shield_builder.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index b632d4a98..d456e4b56 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -386,9 +386,7 @@ impl SecurityShieldBuilder { ) -> Option { self.get(|builder| builder.validate().map(|x| x.into())) } -} -impl SecurityShieldBuilder { - /// TODO: SHOULD `uniffi::export` this!!!!! + pub fn build( self: Arc, ) -> Result< From 80e2c23885bdb2ad4983907d947b9cd5d0dd866e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 21:32:08 +0100 Subject: [PATCH 48/70] still compiles? WIP MethodTooLargeException - `self: Arc` back to `&self` --- .../security_shield_builder.rs | 181 ++++++++---------- 1 file changed, 83 insertions(+), 98 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index d456e4b56..3d75cb5ea 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -31,7 +31,7 @@ impl SecurityShieldBuilder { impl SecurityShieldBuilder { fn get( - self: Arc, + &self, access: impl Fn(&sargon::SecurityShieldBuilder) -> R, ) -> R { let binding = self.wrapped.read().unwrap(); @@ -39,7 +39,7 @@ impl SecurityShieldBuilder { } fn set( - self: Arc, + &self, mut write: impl FnMut( &mut sargon::SecurityShieldBuilder, ) -> &sargon::SecurityShieldBuilder, @@ -49,7 +49,7 @@ impl SecurityShieldBuilder { } fn validation_for_addition_of_factor_source_by_calling( - self: Arc, + &self, factor_sources: Vec, call: impl Fn( &sargon::SecurityShieldBuilder, @@ -74,7 +74,7 @@ impl SecurityShieldBuilder { impl SecurityShieldBuilder { fn get_factors( - self: Arc, + &self, access: impl Fn( &sargon::SecurityShieldBuilder, ) -> Vec, @@ -93,35 +93,31 @@ impl SecurityShieldBuilder { // ==================== #[uniffi::export] impl SecurityShieldBuilder { - pub fn get_primary_threshold(self: Arc) -> u8 { + pub fn get_primary_threshold(&self) -> u8 { self.get(|builder| builder.get_threshold()) } - pub fn get_number_of_days_until_auto_confirm(self: Arc) -> u16 { + pub fn get_number_of_days_until_auto_confirm(&self) -> u16 { self.get(|builder| builder.get_number_of_days_until_auto_confirm()) } - pub fn get_name(self: Arc) -> String { + pub fn get_name(&self) -> String { self.get(|builder| builder.get_name()) } - pub fn get_primary_threshold_factors( - self: Arc, - ) -> Vec { + pub fn get_primary_threshold_factors(&self) -> Vec { self.get_factors(|builder| builder.get_primary_threshold_factors()) } - pub fn get_primary_override_factors( - self: Arc, - ) -> Vec { + pub fn get_primary_override_factors(&self) -> Vec { self.get_factors(|builder| builder.get_primary_override_factors()) } - pub fn get_recovery_factors(self: Arc) -> Vec { + pub fn get_recovery_factors(&self) -> Vec { self.get_factors(|builder| builder.get_recovery_factors()) } - pub fn get_confirmation_factors(self: Arc) -> Vec { + pub fn get_confirmation_factors(&self) -> Vec { self.get_factors(|builder| builder.get_confirmation_factors()) } } @@ -131,12 +127,12 @@ impl SecurityShieldBuilder { // ==================== #[uniffi::export] impl SecurityShieldBuilder { - pub fn set_name(self: Arc, name: String) { + pub fn set_name(&self, name: String) { self.set(|builder| builder.set_name(&name)); } pub fn remove_factor_from_all_roles( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -145,17 +141,14 @@ impl SecurityShieldBuilder { }) } - pub fn remove_factor_from_primary( - self: Arc, - factor_source_id: FactorSourceID, - ) { + pub fn remove_factor_from_primary(&self, factor_source_id: FactorSourceID) { self.set(|builder| { builder.remove_factor_from_primary(factor_source_id.clone().into()) }) } pub fn remove_factor_from_recovery( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -164,7 +157,7 @@ impl SecurityShieldBuilder { } pub fn remove_factor_from_confirmation( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -174,14 +167,11 @@ impl SecurityShieldBuilder { }) } - pub fn set_threshold(self: Arc, threshold: u8) { + pub fn set_threshold(&self, threshold: u8) { self.set(|builder| builder.set_threshold(threshold)) } - pub fn set_number_of_days_until_auto_confirm( - self: Arc, - number_of_days: u16, - ) { + pub fn set_number_of_days_until_auto_confirm(&self, number_of_days: u16) { self.set(|builder| { builder.set_number_of_days_until_auto_confirm(number_of_days) }) @@ -189,7 +179,7 @@ impl SecurityShieldBuilder { /// Adds the factor source to the primary role threshold list. pub fn add_factor_source_to_primary_threshold( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -200,7 +190,7 @@ impl SecurityShieldBuilder { } pub fn add_factor_source_to_primary_override( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -211,7 +201,7 @@ impl SecurityShieldBuilder { } pub fn add_factor_source_to_recovery_override( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -222,7 +212,7 @@ impl SecurityShieldBuilder { } pub fn add_factor_source_to_confirmation_override( - self: Arc, + &self, factor_source_id: FactorSourceID, ) { self.set(|builder| { @@ -236,7 +226,7 @@ impl SecurityShieldBuilder { #[uniffi::export] impl SecurityShieldBuilder { pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -247,7 +237,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_primary_threshold_is_valid_or_can_be( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -258,7 +248,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -269,7 +259,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_primary_override_is_valid_or_can_be( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -280,7 +270,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_recovery_is_valid_or_can_be( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -291,7 +281,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| { @@ -303,7 +293,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_confirmation_is_valid_or_can_be( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -314,7 +304,7 @@ impl SecurityShieldBuilder { } pub fn addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - self: Arc, + &self, factor_source_kind: FactorSourceKind, ) -> bool { self.get(|builder| @@ -328,7 +318,7 @@ impl SecurityShieldBuilder { #[uniffi::export] impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_threshold_for_each( - self: Arc, + &self, factor_sources: Vec, ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( @@ -341,7 +331,7 @@ impl SecurityShieldBuilder { } pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( - self: Arc, + &self, factor_sources: Vec, ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( @@ -353,7 +343,7 @@ impl SecurityShieldBuilder { } pub fn validation_for_addition_of_factor_source_to_recovery_override_for_each( - self: Arc, + &self, factor_sources: Vec, ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( @@ -365,7 +355,7 @@ impl SecurityShieldBuilder { ) } pub fn validation_for_addition_of_factor_source_to_confirmation_override_for_each( - self: Arc, + &self, factor_sources: Vec, ) -> Vec { self.validation_for_addition_of_factor_source_by_calling( @@ -381,14 +371,12 @@ impl SecurityShieldBuilder { #[uniffi::export] impl SecurityShieldBuilder { - pub fn validate( - self: Arc, - ) -> Option { + pub fn validate(&self) -> Option { self.get(|builder| builder.validate().map(|x| x.into())) } pub fn build( - self: Arc, + &self, ) -> Result< SecurityStructureOfFactorSourceIDs, SecurityShieldBuilderInvalidReason, @@ -444,54 +432,51 @@ mod tests { fn test() { let sut = SUT::new(); - assert_eq!(sut.clone().get_name(), "My Shield"); - sut.clone().set_name("S.H.I.E.L.D.".to_owned()); + assert_eq!(sut.get_name(), "My Shield"); + sut.set_name("S.H.I.E.L.D.".to_owned()); - assert_eq!(sut.clone().get_number_of_days_until_auto_confirm(), 14); - sut.clone().set_number_of_days_until_auto_confirm(u16::MAX); - assert_eq!( - sut.clone().get_number_of_days_until_auto_confirm(), - u16::MAX - ); + assert_eq!(sut.get_number_of_days_until_auto_confirm(), 14); + sut.set_number_of_days_until_auto_confirm(u16::MAX); + assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); // Primary // let sim_prim = - // sut.clone().validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + // sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ // FactorSourceID::sample_arculus(), // ]); - // let sim_prim_threshold = sut.clone() + // let sim_prim_threshold = sut // .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ // FactorSourceID::sample_arculus(), // ]); - // let sim_kind_prim = sut.clone() + // let sim_kind_prim = sut // .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( // FactorSourceKind::Device, // ); - // let sim_kind_prim_threshold = sut.clone() + // let sim_kind_prim_threshold = sut // .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( // FactorSourceKind::Device, // ); - sut.clone().add_factor_source_to_primary_threshold( + sut.add_factor_source_to_primary_threshold( FactorSourceID::sample_device(), ); assert_eq!( - sut.clone().get_primary_threshold_factors(), + sut.get_primary_threshold_factors(), vec![FactorSourceID::sample_device()] ); - sut.clone().set_threshold(1); - assert_eq!(sut.clone().get_primary_threshold(), 1); - sut.clone().add_factor_source_to_primary_override( + sut.set_threshold(1); + assert_eq!(sut.get_primary_threshold(), 1); + sut.add_factor_source_to_primary_override( FactorSourceID::sample_arculus(), ); - sut.clone().add_factor_source_to_primary_override( + sut.add_factor_source_to_primary_override( FactorSourceID::sample_arculus_other(), ); assert_eq!( - sut.clone().get_primary_override_factors(), + sut.get_primary_override_factors(), vec![ FactorSourceID::sample_arculus(), FactorSourceID::sample_arculus_other() @@ -500,7 +485,7 @@ mod tests { // Recovery // let sim_rec = - // sut.clone().validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + // sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ // FactorSourceID::sample_ledger(), // ]); @@ -510,15 +495,15 @@ mod tests { // FactorSourceKind::ArculusCard, // ); - sut.clone().add_factor_source_to_recovery_override( + sut.add_factor_source_to_recovery_override( FactorSourceID::sample_ledger(), ); - sut.clone().add_factor_source_to_recovery_override( + sut.add_factor_source_to_recovery_override( FactorSourceID::sample_ledger_other(), ); assert_eq!( - sut.clone().get_recovery_factors(), + sut.get_recovery_factors(), vec![ FactorSourceID::sample_ledger(), FactorSourceID::sample_ledger_other() @@ -526,7 +511,7 @@ mod tests { ); // Confirmation - // let sim_conf = sut.clone() + // let sim_conf = sut // .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ // FactorSourceID::sample_device(), // ]); @@ -537,12 +522,12 @@ mod tests { // FactorSourceKind::ArculusCard, // ); - sut.clone().add_factor_source_to_confirmation_override( + sut.add_factor_source_to_confirmation_override( FactorSourceID::sample_device(), ); assert_eq!( - sut.clone().get_confirmation_factors(), + sut.get_confirmation_factors(), vec![FactorSourceID::sample_device(),] ); /* @@ -551,89 +536,89 @@ mod tests { assert_ne!( sim_prim, - sut.clone().validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ FactorSourceID::sample_arculus(), ]) ); assert_ne!( sim_prim_threshold, - sut.clone().validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ FactorSourceID::sample_arculus() ]) ); assert_ne!( sim_rec, - sut.clone().validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ FactorSourceID::sample_ledger(), ]) ); assert_ne!( sim_conf, - sut.clone().validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ FactorSourceID::sample_device(), ]) ); assert_ne!( sim_kind_prim, - sut.clone().addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( FactorSourceKind::Device, ) ); assert_ne!( sim_kind_prim_threshold, - sut.clone().addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( FactorSourceKind::Device, ) ); assert_eq!( sim_kind_rec, - sut.clone().addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( FactorSourceKind::ArculusCard, ) ); assert_eq!( sim_kind_conf, - sut.clone().addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( FactorSourceKind::ArculusCard, ) ); */ - sut.clone().remove_factor_from_all_roles( + sut.remove_factor_from_all_roles( FactorSourceID::sample_arculus_other(), ); - sut.clone().remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); + sut.remove_factor_from_all_roles(FactorSourceID::sample_ledger_other()); let f = FactorSourceID::sample_ledger_other(); - let xs = sut.clone().get_primary_override_factors(); - sut.clone().add_factor_source_to_primary_override(f.clone()); - sut.clone().remove_factor_from_primary(f.clone()); - assert_eq!(xs, sut.clone().get_primary_override_factors()); + let xs = sut.get_primary_override_factors(); + sut.add_factor_source_to_primary_override(f.clone()); + sut.remove_factor_from_primary(f.clone()); + assert_eq!(xs, sut.get_primary_override_factors()); - let xs = sut.clone().get_recovery_factors(); + let xs = sut.get_recovery_factors(); sut.clone() .add_factor_source_to_recovery_override(f.clone()); - sut.clone().remove_factor_from_recovery(f.clone()); - assert_eq!(xs, sut.clone().get_recovery_factors()); + sut.remove_factor_from_recovery(f.clone()); + assert_eq!(xs, sut.get_recovery_factors()); - let xs = sut.clone().get_confirmation_factors(); + let xs = sut.get_confirmation_factors(); sut.clone() .add_factor_source_to_confirmation_override(f.clone()); - sut.clone().remove_factor_from_confirmation(f.clone()); - assert_eq!(xs, sut.clone().get_confirmation_factors()); + sut.remove_factor_from_confirmation(f.clone()); + assert_eq!(xs, sut.get_confirmation_factors()); - let v0 = sut.clone().validate(); - let v1 = sut.clone().validate(); // can call validate many times! + let v0 = sut.validate(); + let v1 = sut.validate(); // can call validate many times! assert_eq!(v0, v1); - let shield0 = sut.clone().build().unwrap(); - let shield = sut.clone().build().unwrap(); // can call build many times! + let shield0 = sut.build().unwrap(); + let shield = sut.build().unwrap(); // can call build many times! assert_eq!(shield0, shield); assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D."); From 092f9bbdc826c6c62a60883b1afe54c8838df4da Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 21:42:49 +0100 Subject: [PATCH 49/70] still compiles? WIP MethodTooLargeException - reintroduce tests --- .../security_shield_builder.rs | 191 +++++++++--------- 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 3d75cb5ea..1869c6db3 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -439,25 +439,25 @@ mod tests { sut.set_number_of_days_until_auto_confirm(u16::MAX); assert_eq!(sut.get_number_of_days_until_auto_confirm(), u16::MAX); // Primary - // let sim_prim = - // sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - // FactorSourceID::sample_arculus(), - // ]); - - // let sim_prim_threshold = sut - // .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - // FactorSourceID::sample_arculus(), - // ]); - - // let sim_kind_prim = sut - // .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - // FactorSourceKind::Device, - // ); - - // let sim_kind_prim_threshold = sut - // .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - // FactorSourceKind::Device, - // ); + let sim_prim = + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]); + + let sim_prim_threshold = sut + .validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus(), + ]); + + let sim_kind_prim = sut + .addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + FactorSourceKind::Device, + ); + + let sim_kind_prim_threshold = sut + .addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + FactorSourceKind::Device, + ); sut.add_factor_source_to_primary_threshold( FactorSourceID::sample_device(), @@ -484,16 +484,16 @@ mod tests { ); // Recovery - // let sim_rec = - // sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - // FactorSourceID::sample_ledger(), - // ]); + let sim_rec = + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]); - // let sim_kind_rec = sut - // .clone() - // .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - // FactorSourceKind::ArculusCard, - // ); + let sim_kind_rec = sut + .clone() + .addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::ArculusCard, + ); sut.add_factor_source_to_recovery_override( FactorSourceID::sample_ledger(), @@ -511,16 +511,16 @@ mod tests { ); // Confirmation - // let sim_conf = sut - // .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - // FactorSourceID::sample_device(), - // ]); + let sim_conf = sut + .validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]); - // let sim_kind_conf = sut - // .clone() - // .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - // FactorSourceKind::ArculusCard, - // ); + let sim_kind_conf = sut + .clone() + .addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::ArculusCard, + ); sut.add_factor_source_to_confirmation_override( FactorSourceID::sample_device(), @@ -530,66 +530,63 @@ mod tests { sut.get_confirmation_factors(), vec![FactorSourceID::sample_device(),] ); - /* - - Reintroduce these asserts when we reintroduce the field `reason_if_invalid` inside `FactorSourceValidationStatus` - - assert_ne!( - sim_prim, - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ - FactorSourceID::sample_arculus(), - ]) - ); - - assert_ne!( - sim_prim_threshold, - sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ - FactorSourceID::sample_arculus() - ]) - ); - - assert_ne!( - sim_rec, - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - FactorSourceID::sample_ledger(), - ]) - ); - - assert_ne!( - sim_conf, - sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ - FactorSourceID::sample_device(), - ]) - ); - - assert_ne!( - sim_kind_prim, - sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( - FactorSourceKind::Device, - ) - ); - - assert_ne!( - sim_kind_prim_threshold, - sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( - FactorSourceKind::Device, - ) - ); - - assert_eq!( - sim_kind_rec, - sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( - FactorSourceKind::ArculusCard, - ) - ); - - assert_eq!( - sim_kind_conf, - sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( - FactorSourceKind::ArculusCard, - ) - ); - */ + + assert_ne!( + sim_prim, + sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + FactorSourceID::sample_arculus(), + ]) + ); + + assert_ne!( + sim_prim_threshold, + sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + FactorSourceID::sample_arculus() + ]) + ); + + assert_ne!( + sim_rec, + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ + FactorSourceID::sample_ledger(), + ]) + ); + + assert_ne!( + sim_conf, + sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + FactorSourceID::sample_device(), + ]) + ); + + assert_ne!( + sim_kind_prim, + sut.addition_of_factor_source_of_kind_to_primary_override_is_fully_valid( + FactorSourceKind::Device, + ) + ); + + assert_ne!( + sim_kind_prim_threshold, + sut.addition_of_factor_source_of_kind_to_primary_threshold_is_fully_valid( + FactorSourceKind::Device, + ) + ); + + assert_eq!( + sim_kind_rec, + sut.addition_of_factor_source_of_kind_to_recovery_is_fully_valid( + FactorSourceKind::ArculusCard, + ) + ); + + assert_eq!( + sim_kind_conf, + sut.addition_of_factor_source_of_kind_to_confirmation_is_fully_valid( + FactorSourceKind::ArculusCard, + ) + ); + sut.remove_factor_from_all_roles( FactorSourceID::sample_arculus_other(), ); From 8c944c169b5fc97419d3369127dda51835701117 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 3 Dec 2024 22:33:01 +0100 Subject: [PATCH 50/70] still compiles? WIP MethodTooLargeException - add String to FactorSourceValidationStatusReasonIfInvalid::BasicViolation as associated value --- .../MFA/SecurityShieldBuilder+Swifified.swift | 17 +-- .../MFA/SecurityShieldsBuilderTests.swift | 112 ++++++++++++++---- ...ource_in_role_builder_validation_status.rs | 10 +- 3 files changed, 97 insertions(+), 42 deletions(-) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift index 47ccc2fd9..189231a54 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/MFA/SecurityShieldBuilder+Swifified.swift @@ -1,19 +1,5 @@ import SargonUniFFI -// extension FactorSourceValidationStatus { -// public var factorSourceID: FactorSourceID { -// factorSourceId() -// } -// -//// public var reasonIfInvalid: SecurityShieldBuilderInvalidReason? { -//// self.reasonIfInvalid() -//// } -// -// public var role: RoleKind { -// role() -// } -// } - extension SecurityShieldBuilder { public typealias Factor = FactorSourceID @@ -27,7 +13,8 @@ extension SecurityShieldBuilder { } public var threshold: UInt8 { - getPrimaryThreshold() + get { getPrimaryThreshold() } + set { setThreshold(threshold: newValue) } } public var primaryRoleThresholdFactors: [Factor] { diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index 0d5d3a0c1..801441bfd 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -4,6 +4,7 @@ import Sargon import SargonUniFFI import Testing +// MARK: - ShieldTests @Suite("ShieldBuilder") struct ShieldTests { @Test("name") @@ -14,23 +15,21 @@ struct ShieldTests { #expect(builder.name == "S.H.I.E.L.D") } -// @Test("threshold") -// func threshold() { -// let builder = SecurityShieldBuilder() -// #expect(builder.threshold == 0) -// #expect(throws: CommonError.RoleMustHaveAtLeastOneFactor) { -// try builder.setThreshold(threshold: 0) -// } -// } -// -// @Test("days") -// func days() { -// let builder = SecurityShieldBuilder() -// #expect(builder.numberOfDaysUntilAutoConfirm == 14) -// #expect(throws: CommonError.NumberOfDaysUntilAutoConfirmMustBeGreaterThanZero) { -// try builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 0) -// } -// } + @Test("threshold") + func threshold() { + let builder = SecurityShieldBuilder() + #expect(builder.threshold == 0) + builder.setThreshold(threshold: 42) + #expect(builder.threshold == 42) + } + + @Test("days") + func days() { + let builder = SecurityShieldBuilder() + #expect(builder.numberOfDaysUntilAutoConfirm == 14) + builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 237) + #expect(builder.numberOfDaysUntilAutoConfirm == 237) + } @Test("empty primary threshold") func emptyThresholdFactors() { @@ -56,9 +55,78 @@ struct ShieldTests { #expect(builder.confirmationRoleFactors == []) } -// @Test("primary override validation status trustedContact") -// func primValidationStatusTrustedContact() throws { -// let builder = SecurityShieldBuilder() -// try #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).map(\.validationError) == [CommonError.PrimaryCannotContainTrustedContact]) -// } + @Test("primary override validation status trustedContact") + func primValidationStatusTrustedContact() { + let builder = SecurityShieldBuilder() + #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).compactMap(\.reasonIfInvalid) == [FactorSourceValidationStatusReasonIfInvalid.nonBasic(SecurityShieldBuilderInvalidReason.PrimaryCannotContainTrustedContact)]) + } + + @Test("Complete") + func complete() throws { + let builder = SecurityShieldBuilder() + builder.setName(name: "S.H.I.E.L.D.") + builder.numberOfDaysUntilAutoConfirm = 42 + + #expect(builder.validate() == .PrimaryRoleMustHaveAtLeastOneFactor) + + // Primary + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) + builder.threshold = 1 + builder.addFactorSourceToPrimaryOverride(factorSourceId: .sampleArculus) + builder.addFactorSourceToPrimaryOverride(factorSourceId: .sampleArculusOther) + + // Recovery + builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleLedger) + builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleLedgerOther) + + // Confirmation + builder.addFactorSourceToConfirmationOverride(factorSourceId: .sampleDevice) + + builder.removeFactorFromPrimary(factorSourceId: .sampleArculusOther) + builder.removeFactorFromRecovery(factorSourceId: .sampleLedgerOther) + + // Validate + #expect(builder.validate() == nil) + + // Build + let shield0 = try builder.build() + let shield = try builder.build() + #expect(shield0 == shield) + + // Assert + #expect(shield.metadata.displayName == "S.H.I.E.L.D.") + #expect(shield.matrixOfFactors.primaryRole.overrideFactors == [.sampleArculus]) + #expect(shield.matrixOfFactors.primaryRole.thresholdFactors == [.sampleDevice]) + + #expect(shield.matrixOfFactors.recoveryRole.overrideFactors == [.sampleLedger]) + #expect(shield.matrixOfFactors.recoveryRole.thresholdFactors == []) + + #expect(shield.matrixOfFactors.confirmationRole.overrideFactors == [.sampleDevice]) + #expect(shield.matrixOfFactors.confirmationRole.thresholdFactors == []) + } +} + +#if DEBUG +extension FactorSourceID { + public static let sampleDevice = DeviceFactorSource.sample.asGeneral.id + public static let sampleDeviceOther = DeviceFactorSource.sampleOther.asGeneral.id + + public static let sampleLedger = LedgerHardwareWalletFactorSource.sample.asGeneral.id + public static let sampleLedgerOther = LedgerHardwareWalletFactorSource.sampleOther.asGeneral.id + + public static let sampleArculus = ArculusCardFactorSource.sample.asGeneral.id + public static let sampleArculusOther = ArculusCardFactorSource.sampleOther.asGeneral.id + + public static let samplePassword = PasswordFactorSource.sample.asGeneral.id + public static let samplePasswordOther = PasswordFactorSource.sampleOther.asGeneral.id + + public static let sampleOffDeviceMnemonic = OffDeviceMnemonicFactorSource.sample.asGeneral.id + public static let sampleOffDeviceMnemonicOther = OffDeviceMnemonicFactorSource.sampleOther.asGeneral.id + + public static let sampleTrustedContact = TrustedContactFactorSource.sample.asGeneral.id + public static let sampleTrustedContactOther = TrustedContactFactorSource.sampleOther.asGeneral.id + + public static let sampleSecurityQuestions = SecurityQuestionsNotProductionReadyFactorSource.sample.asGeneral.id + public static let sampleSecurityQuestionsOther = SecurityQuestionsNotProductionReadyFactorSource.sampleOther.asGeneral.id } +#endif diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index 98da53a1d..f386f65ad 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -11,7 +11,7 @@ pub struct FactorSourceValidationStatus { #[derive(Clone, Debug, PartialEq, uniffi::Enum)] pub enum FactorSourceValidationStatusReasonIfInvalid { - BasicViolation, + BasicViolation(String), NonBasic(SecurityShieldBuilderInvalidReason), } @@ -24,10 +24,10 @@ impl From > = { match val.validation { Ok(_) => None, - Err(sargon::RoleBuilderValidation::BasicViolation(_)) => Some( - FactorSourceValidationStatusReasonIfInvalid::BasicViolation, // FactorSourceValidationStatusReasonIfInvalid::BasicViolation( - // format!("{:?}", b), - // ), + Err(sargon::RoleBuilderValidation::BasicViolation(b)) => Some( + FactorSourceValidationStatusReasonIfInvalid::BasicViolation( + format!("{:?}", b), + ), ), Err(sargon::RoleBuilderValidation::ForeverInvalid(v)) => v .as_shield_validation() From ac9550f1f9fdf24b8e8f747c0dd0a2e93f05d29e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 08:03:24 +0100 Subject: [PATCH 51/70] CannotSetThreshold err into BasicViolation only --- .../security_shield_builder_invalid_reason.rs | 6 --- .../roles/builder/roles_builder.rs | 45 +++++++------------ .../security_shield_builder_invalid_reason.rs | 12 ----- 3 files changed, 17 insertions(+), 46 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index c094a3cc2..ef7ee1b95 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -66,18 +66,12 @@ pub enum SecurityShieldBuilderInvalidReason { #[error("Primary role cannot contain Trusted Contact")] PrimaryCannotContainTrustedContact, - #[error("Recovery role threshold list not supported")] - RecoveryRoleThresholdFactorsNotSupported, - #[error("Recovery role Security Questions not supported")] RecoveryRoleSecurityQuestionsNotSupported, #[error("Recovery role password not supported")] RecoveryRolePasswordNotSupported, - #[error("Confirmation role threshold list not supported")] - ConfirmationRoleThresholdFactorsNotSupported, - #[error("Confirmation role cannot contain Trusted Contact")] ConfirmationRoleTrustedContactNotSupported, } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index dc780d1e9..478f0713b 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -100,18 +100,12 @@ pub enum ForeverInvalidReason { #[error("Primary role cannot contain Trusted Contact")] PrimaryCannotContainTrustedContact, - #[error("Recovery role threshold list not supported")] - RecoveryRoleThresholdFactorsNotSupported, - #[error("Recovery role Security Questions not supported")] RecoveryRoleSecurityQuestionsNotSupported, #[error("Recovery role password not supported")] RecoveryRolePasswordNotSupported, - #[error("Confirmation role threshold list not supported")] - ConfirmationRoleThresholdFactorsNotSupported, - #[error("Confirmation role cannot contain Trusted Contact")] ConfirmationRoleTrustedContactNotSupported, } @@ -143,17 +137,13 @@ impl FromBasicViolation for std::result::Result { } } -impl ForeverInvalidReason { +impl BasicViolation { pub(crate) fn threshold_list_not_supported_for_role( role: RoleKind, ) -> Self { match role { - RoleKind::Recovery => { - Self::RecoveryRoleThresholdFactorsNotSupported - } - RoleKind::Confirmation => { - Self::ConfirmationRoleThresholdFactorsNotSupported - } + RoleKind::Recovery => Self::RecoveryCannotSetThreshold, + RoleKind::Confirmation => Self::ConfirmationCannotSetThreshold, RoleKind::Primary => { unreachable!("Primary role DOES support threshold list. This is programmer error.") } @@ -332,16 +322,17 @@ impl RoleBuilder { } RoleKind::Recovery | RoleKind::Confirmation => match factor_list_kind { Threshold => { - return Result::forever_invalid( - ForeverInvalidReason::threshold_list_not_supported_for_role(self.role()), + return Result::basic_violation( + BasicViolation::threshold_list_not_supported_for_role(self.role()), + ) + } + Override => { + self.validation_for_addition_of_factor_source_of_kind_to_override_for_non_primary_role( + factor_source_kind, ) } - Override => {} }, } - self.validation_for_addition_of_factor_source_of_kind_to_override_for_non_primary_role( - factor_source_kind, - ) } } @@ -825,9 +816,7 @@ mod tests { ); assert_eq!( res, - Err(ForeverInvalid( - ForeverInvalidReason::RecoveryRoleThresholdFactorsNotSupported - )) + Err(BasicViolation(BasicViolation::RecoveryCannotSetThreshold)) ); } @@ -840,8 +829,8 @@ mod tests { ); assert_eq!( res, - Err(ForeverInvalid( - ForeverInvalidReason::ConfirmationRoleThresholdFactorsNotSupported + Err(BasicViolation( + BasicViolation::ConfirmationCannotSetThreshold )) ); } @@ -852,8 +841,8 @@ mod tests { let res = sut._validation_add(FactorSourceKind::Device, Threshold); assert_eq!( res, - RoleBuilderMutateResult::forever_invalid( - ForeverInvalidReason::threshold_list_not_supported_for_role( + RoleBuilderMutateResult::basic_violation( + BasicViolation::threshold_list_not_supported_for_role( RoleKind::Recovery ) ) @@ -866,8 +855,8 @@ mod tests { let res = sut._validation_add(FactorSourceKind::Device, Threshold); assert_eq!( res, - RoleBuilderMutateResult::forever_invalid( - ForeverInvalidReason::threshold_list_not_supported_for_role( + RoleBuilderMutateResult::basic_violation( + BasicViolation::threshold_list_not_supported_for_role( RoleKind::Confirmation ) ) diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index f594173a5..7ee6b3335 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -121,18 +121,12 @@ impl AsShieldBuilderViolation for ForeverInvalidReason { PrimaryCannotContainTrustedContact => { SecurityShieldBuilderInvalidReason::PrimaryCannotContainTrustedContact } - RecoveryRoleThresholdFactorsNotSupported => { - SecurityShieldBuilderInvalidReason::RecoveryRoleThresholdFactorsNotSupported - } RecoveryRoleSecurityQuestionsNotSupported => { SecurityShieldBuilderInvalidReason::RecoveryRoleSecurityQuestionsNotSupported } RecoveryRolePasswordNotSupported => { SecurityShieldBuilderInvalidReason::RecoveryRolePasswordNotSupported } - ConfirmationRoleThresholdFactorsNotSupported => { - SecurityShieldBuilderInvalidReason::ConfirmationRoleThresholdFactorsNotSupported - } ConfirmationRoleTrustedContactNotSupported => { SecurityShieldBuilderInvalidReason::ConfirmationRoleTrustedContactNotSupported } @@ -243,18 +237,12 @@ pub enum SecurityShieldBuilderInvalidReason { #[error("Primary role cannot contain Trusted Contact")] PrimaryCannotContainTrustedContact, - #[error("Recovery role threshold list not supported")] - RecoveryRoleThresholdFactorsNotSupported, - #[error("Recovery role Security Questions not supported")] RecoveryRoleSecurityQuestionsNotSupported, #[error("Recovery role password not supported")] RecoveryRolePasswordNotSupported, - #[error("Confirmation role threshold list not supported")] - ConfirmationRoleThresholdFactorsNotSupported, - #[error("Confirmation role cannot contain Trusted Contact")] ConfirmationRoleTrustedContactNotSupported, } From c890bc1a27dfefb6cc7f06c5e8403ffc1938d837 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 09:01:26 +0100 Subject: [PATCH 52/70] polish --- .../MFA/SecurityShieldsBuilderTests.swift | 28 +++++ .../roles/builder/roles_builder.rs | 109 +++++++++--------- 2 files changed, 84 insertions(+), 53 deletions(-) diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index 801441bfd..2f0e10b21 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -60,6 +60,34 @@ struct ShieldTests { let builder = SecurityShieldBuilder() #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).compactMap(\.reasonIfInvalid) == [FactorSourceValidationStatusReasonIfInvalid.nonBasic(SecurityShieldBuilderInvalidReason.PrimaryCannotContainTrustedContact)]) } + + @Test("Auto lowering of threshold upon deletion") + func deleteFactorSourceFromPrimaryLowersThreshold() { + let builder = SecurityShieldBuilder() + let x: FactorSourceID = .sampleDevice + let y: FactorSourceID = .sampleLedger + let z: FactorSourceID = .sampleArculus + builder.addFactorSourceToPrimaryThreshold(factorSourceId: x) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: y) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: z) + builder.threshold = 3 + + builder.addFactorSourceToRecoveryOverride(factorSourceId: y) + #expect(builder.recoveryRoleFactors == [y]) + + #expect(builder.threshold == 3) + + builder.removeFactorFromPrimary(factorSourceId: x) + #expect(builder.threshold == 2) + + builder.removeFactorFromAllRoles(factorSourceId: y) + #expect(builder.recoveryRoleFactors == []) // assert `y` is removed from Recovery and Primary + #expect(builder.threshold == 1) + + builder.removeFactorFromPrimary(factorSourceId: z) + #expect(builder.threshold == 0) + #expect(builder.primaryRoleThresholdFactors == []) + } @Test("Complete") func complete() throws { diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 478f0713b..26a27e903 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -218,8 +218,7 @@ impl RoleBuilder where Assert<{ ROLE == ROLE_PRIMARY }>: IsTrue, { - /// If Ok => self is mutated - /// If Err(NotYetValid) => self is mutated + /// If Ok | Err(NotYetValid) => self is mutated /// If Err(ForeverInvalid) => self is not mutated pub(crate) fn add_factor_source_to_threshold( &mut self, @@ -251,9 +250,10 @@ impl RoleBuilder where Assert<{ ROLE > ROLE_PRIMARY }>: IsTrue, { - /// If Ok => self is mutated - /// If Err(NotYetValid) => self is mutated - /// If Err(ForeverInvalid) => self is not mutated + /// ```ignore + /// Ok | Err(NotYetValid) => self is mutated + /// Err(ForeverInvalid) => self is NOT mutated + /// ``` pub(crate) fn add_factor_source( &mut self, factor_source_id: FactorSourceID, @@ -263,9 +263,10 @@ where } impl RoleBuilder { - /// If Ok => self is mutated - /// If Err(NotYetValid) => self is mutated - /// If Err(ForeverInvalid) => self is not mutated + /// ```ignore + /// Ok | Err(NotYetValid) => self is mutated + /// Err(ForeverInvalid) => self is NOT mutated + /// ``` pub(crate) fn add_factor_source_to_override( &mut self, factor_source_id: FactorSourceID, @@ -273,9 +274,10 @@ impl RoleBuilder { self._add_factor_source_to_list(factor_source_id, Override) } - /// If Ok => self is mutated - /// If Err(NotYetValid) => self is mutated - /// If Err(ForeverInvalid) => self is not mutated + /// ```ignore + /// Ok | Err(NotYetValid) => self is mutated + /// Err(ForeverInvalid) => self is NOT mutated + /// ``` fn _add_factor_source_to_list( &mut self, factor_source_id: FactorSourceID, @@ -315,14 +317,14 @@ impl RoleBuilder { ) -> RoleBuilderMutateResult { match self.role() { RoleKind::Primary => { - return self.validation_for_addition_of_factor_source_of_kind_to_list_for_primary( + self.validation_for_addition_of_factor_source_of_kind_to_list_for_primary( factor_source_kind, factor_list_kind, ) } RoleKind::Recovery | RoleKind::Confirmation => match factor_list_kind { Threshold => { - return Result::basic_violation( + Result::basic_violation( BasicViolation::threshold_list_not_supported_for_role(self.role()), ) } @@ -640,46 +642,46 @@ impl RoleBuilder { } #[cfg(not(tarpaulin_include))] // false negative - fn validation_for_addition_of_factor_source_of_kind_to_override_for_confirmation( + fn validation_for_addition_of_factor_source_of_kind_to_override_for_recovery( &self, factor_source_kind: FactorSourceKind, ) -> RoleBuilderMutateResult { - assert_eq!(self.role(), RoleKind::Confirmation); + assert_eq!(self.role(), RoleKind::Recovery); match factor_source_kind { FactorSourceKind::Device | FactorSourceKind::LedgerHQHardwareWallet | FactorSourceKind::ArculusCard - | FactorSourceKind::Password | FactorSourceKind::OffDeviceMnemonic - | FactorSourceKind::SecurityQuestions => Ok(()), - FactorSourceKind::TrustedContact => { + | FactorSourceKind::TrustedContact => Ok(()), + FactorSourceKind::SecurityQuestions => { RoleBuilderMutateResult::forever_invalid( - ConfirmationRoleTrustedContactNotSupported, + RecoveryRoleSecurityQuestionsNotSupported, + ) + } + FactorSourceKind::Password => { + RoleBuilderMutateResult::forever_invalid( + RecoveryRolePasswordNotSupported, ) } } } #[cfg(not(tarpaulin_include))] // false negative - fn validation_for_addition_of_factor_source_of_kind_to_override_for_recovery( + fn validation_for_addition_of_factor_source_of_kind_to_override_for_confirmation( &self, factor_source_kind: FactorSourceKind, ) -> RoleBuilderMutateResult { - assert_eq!(self.role(), RoleKind::Recovery); + assert_eq!(self.role(), RoleKind::Confirmation); match factor_source_kind { FactorSourceKind::Device | FactorSourceKind::LedgerHQHardwareWallet | FactorSourceKind::ArculusCard + | FactorSourceKind::Password | FactorSourceKind::OffDeviceMnemonic - | FactorSourceKind::TrustedContact => Ok(()), - FactorSourceKind::SecurityQuestions => { - RoleBuilderMutateResult::forever_invalid( - RecoveryRoleSecurityQuestionsNotSupported, - ) - } - FactorSourceKind::Password => { + | FactorSourceKind::SecurityQuestions => Ok(()), + FactorSourceKind::TrustedContact => { RoleBuilderMutateResult::forever_invalid( - RecoveryRolePasswordNotSupported, + ConfirmationRoleTrustedContactNotSupported, ) } } @@ -695,31 +697,32 @@ impl RoleBuilder { factor_list_kind: FactorListKind, ) -> RoleBuilderMutateResult { assert_eq!(self.role(), RoleKind::Primary); + + if factor_list_kind != Threshold { + return RoleBuilderMutateResult::forever_invalid( + PrimaryCannotHavePasswordInOverrideList, + ); + } + let factor_source_kind = FactorSourceKind::Password; - match factor_list_kind { - Threshold => { - let is_alone = self - .factor_sources_not_of_kind_to_list_of_kind( - factor_source_kind, - Threshold, - ) - .is_empty(); - if is_alone { - return RoleBuilderMutateResult::not_yet_valid( - PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, - ); - } - if self.get_threshold() < 2 { - return RoleBuilderMutateResult::not_yet_valid( - PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, - ); - } - } - Override => { - return RoleBuilderMutateResult::forever_invalid( - PrimaryCannotHavePasswordInOverrideList, - ); - } + + let is_alone = self + .factor_sources_not_of_kind_to_list_of_kind( + factor_source_kind, + Threshold, + ) + .is_empty(); + + if is_alone { + return RoleBuilderMutateResult::not_yet_valid( + PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor, + ); + } + + if self.get_threshold() < 2 { + return RoleBuilderMutateResult::not_yet_valid( + PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, + ); } Ok(()) From 72567130992fcbe137c1f933761c6ec51a4c84e7 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 09:39:39 +0100 Subject: [PATCH 53/70] Change to const for marker if AbstractRoleBuilderOrBuilt and AbstractMatrixBuilderOrBuilt. Allowing us to drop PhantomData --- .../factor_instances_provider_unit_tests.rs | 15 ------ .../abstract_matrix_builder_or_built.rs | 54 +++++++++++-------- .../matrices/builder/matrix_builder.rs | 37 ++++++++----- .../matrices/builder/matrix_template.rs | 30 ++++++----- .../matrices/matrix_of_factor_instances.rs | 17 +++--- .../matrices/matrix_of_factor_sources.rs | 18 ++++--- .../roles/abstract_role_builder_or_built.rs | 20 +++---- .../security_structure_of_factor_sources.rs | 9 ++-- 8 files changed, 102 insertions(+), 98 deletions(-) diff --git a/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs b/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs index 35fa88036..8acc7238b 100644 --- a/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs +++ b/crates/sargon/src/factor_instances_provider/provider/factor_instances_provider_unit_tests.rs @@ -463,7 +463,6 @@ async fn cache_is_unchanged_in_case_of_failure() { assert_eq!(all_accounts.len(), 3 * n); let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -616,7 +615,6 @@ async fn test_assert_factor_instances_invalid() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -955,7 +953,6 @@ async fn test_securified_accounts() { os.add_factor_source(password.clone()).await.unwrap(); let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 2, [bdfs.clone(), ledger.clone(), arculus.clone()], @@ -1090,7 +1087,6 @@ async fn test_securified_accounts() { ); let matrix_1 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [password.clone()], @@ -1226,7 +1222,6 @@ async fn securify_accounts_when_cache_is_half_full_single_factor_source() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -1374,7 +1369,6 @@ async fn securify_accounts_when_cache_is_half_full_multiple_factor_sources() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 2, [bdfs.clone(), ledger.clone(), arculus.clone()], @@ -1612,7 +1606,6 @@ async fn securify_personas_when_cache_is_half_full_single_factor_source() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -1746,7 +1739,6 @@ async fn create_single_account() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -1836,7 +1828,6 @@ async fn securified_personas() { os.add_factor_source(password.clone()).await.unwrap(); let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 2, [bdfs.clone(), ledger.clone(), arculus.clone()], @@ -1975,7 +1966,6 @@ async fn securified_personas() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_1 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [password.clone()], @@ -2139,7 +2129,6 @@ async fn securified_all_accounts_next_veci_does_not_start_at_zero() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -2341,7 +2330,6 @@ async fn securified_accounts_asymmetric_indices() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_0 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 1, [bdfs.clone()], @@ -2455,7 +2443,6 @@ async fn securified_accounts_asymmetric_indices() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_1 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 2, [bdfs.clone(), arculus.clone()], @@ -2521,7 +2508,6 @@ async fn securified_accounts_asymmetric_indices() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_2 = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 2, [bdfs.clone(), ledger.clone()], @@ -2629,7 +2615,6 @@ async fn securified_accounts_asymmetric_indices() { // We are not testing valid matrices here... we are testing the factor // instances provider... let matrix_3fa = MatrixOfFactorSources { - built: PhantomData, primary_role: PrimaryRoleWithFactorSources::with_factors( 2, [bdfs.clone(), ledger.clone(), arculus.clone()], diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs index cb99083cf..534dbd244 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/abstract_matrix_builder_or_built.rs @@ -2,6 +2,17 @@ use crate::prelude::*; +/// One of two possible `MODE_OF_MATRIX` values, used for the **builder of a matrix**. +pub const IS_MATRIX_BUILDER: u8 = 0; +/// One of two possible `MODE_OF_MATRIX` values, used for the **built matrix**. +pub const IS_BUILT_MATRIX: u8 = 1; + +/// One of two possible `MODE_OF_ROLE` values, used for the **builder of roles**. +pub const IS_ROLE_BUILDER: u8 = 0; + +/// One of two possible `MODE_OF_ROLE` values, used for the **built roles**. +pub const IS_BUILT_ROLE: u8 = 0; + /// Either a matrix or a **builder of a matrix** with a Primary, Recovery and Confirmation /// role or **builder of roles**. /// This type is shared by: @@ -16,60 +27,59 @@ use crate::prelude::*; /// it is `PhantomData`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct AbstractMatrixBuilderOrBuilt { - #[serde(skip)] - #[doc(hidden)] - pub(crate) built: PhantomData, - +pub struct AbstractMatrixBuilderOrBuilt< + const MODE_OF_MATRIX: u8, + const MODE_OF_ROLE: u8, + FACTOR, +> { pub(crate) primary_role: - AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, FACTOR, BUILT_ROLE>, + AbstractRoleBuilderOrBuilt<{ ROLE_PRIMARY }, MODE_OF_ROLE, FACTOR>, pub(crate) recovery_role: - AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, FACTOR, BUILT_ROLE>, + AbstractRoleBuilderOrBuilt<{ ROLE_RECOVERY }, MODE_OF_ROLE, FACTOR>, pub(crate) confirmation_role: - AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, FACTOR, BUILT_ROLE>, + AbstractRoleBuilderOrBuilt<{ ROLE_CONFIRMATION }, MODE_OF_ROLE, FACTOR>, pub number_of_days_until_auto_confirm: u16, } -impl - AbstractMatrixBuilderOrBuilt +impl + AbstractMatrixBuilderOrBuilt { pub const DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM: u16 = 14; /// # Safety /// Rust memory safe, but marked "unsafe" since it might allow for instantiation - /// of unsafe - as in application **unsecure** - MatrixofFactors, which might + /// of unsafe - as in application **unsecure** - MatrixOfFactors, which might /// lead to increase risk for end user to loose funds. pub unsafe fn unbuilt_with_roles_and_days( - primary: AbstractRoleBuilderOrBuilt< + primary_role: AbstractRoleBuilderOrBuilt< { ROLE_PRIMARY }, + MODE_OF_ROLE, FACTOR, - BUILT_ROLE, >, - recovery: AbstractRoleBuilderOrBuilt< + recovery_role: AbstractRoleBuilderOrBuilt< { ROLE_RECOVERY }, + MODE_OF_ROLE, FACTOR, - BUILT_ROLE, >, - confirmation: AbstractRoleBuilderOrBuilt< + confirmation_role: AbstractRoleBuilderOrBuilt< { ROLE_CONFIRMATION }, + MODE_OF_ROLE, FACTOR, - BUILT_ROLE, >, number_of_days_until_auto_confirm: u16, ) -> Self { Self { - built: PhantomData, - primary_role: primary, - recovery_role: recovery, - confirmation_role: confirmation, + primary_role, + recovery_role, + confirmation_role, number_of_days_until_auto_confirm, } } } pub type AbstractMatrixBuilt = - AbstractMatrixBuilderOrBuilt; + AbstractMatrixBuilderOrBuilt; impl AbstractMatrixBuilt { pub fn primary( diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index 2f0dd4ddf..1ac9444d7 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -3,17 +3,20 @@ use crate::prelude::*; pub type MatrixBuilderMutateResult = Result<(), MatrixBuilderValidation>; + pub type MatrixBuilderBuildResult = Result; -/// Marker to distinguish between built and builder. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Built; - +/// A builder of MatrixOfFactorSourceIds, consists of role builders: +/// * PrimaryRoleBuilder +/// * RecoveryRoleBuilder +/// * ConfirmationRoleBuilder +/// +/// And `number_of_days_until_auto_confirm`. pub type MatrixBuilder = AbstractMatrixBuilderOrBuilt< + IS_MATRIX_BUILDER, + IS_ROLE_BUILDER, FactorSourceID, - MatrixOfFactorSourceIds, - Built, // Marker to distinguish between built and builder. >; // ================== @@ -22,7 +25,6 @@ pub type MatrixBuilder = AbstractMatrixBuilderOrBuilt< impl MatrixBuilder { pub fn new() -> Self { Self { - built: PhantomData, primary_role: PrimaryRoleBuilder::new(), recovery_role: RecoveryRoleBuilder::new(), confirmation_role: ConfirmationRoleBuilder::new(), @@ -31,6 +33,9 @@ impl MatrixBuilder { } } + /// Validates each role in isolation and all roles in combination. + /// + /// If valid it returns a "built" `MatrixOfFactorSourceIds`. pub fn build(&self) -> MatrixBuilderBuildResult { self.validate_combination()?; @@ -47,13 +52,17 @@ impl MatrixBuilder { .build() .into_matrix_err(RoleKind::Confirmation)?; - let built = MatrixOfFactorSourceIds { - built: PhantomData, - primary_role: primary, - recovery_role: recovery, - confirmation_role: confirmation, - number_of_days_until_auto_confirm: self - .number_of_days_until_auto_confirm, + let built = unsafe { + // Looks a bit odd, but yeah here is in fact the only place we + // do build! The ctor is named like that so that it is clear that + // when used elsewhere, it is not guaranteed to have been properly + // built. + MatrixOfFactorSourceIds::unbuilt_with_roles_and_days( + primary, + recovery, + confirmation, + self.number_of_days_until_auto_confirm, + ) }; Ok(built) } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs index 0ff1c7410..8d76924c4 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -84,18 +84,25 @@ impl MatrixTemplate { ) -> Result { let number_of_days_until_auto_confirm = self.number_of_days_until_auto_confirm; + let mut assigner = FactorSourceIdAssigner::new(factor_source_ids); + let primary_role = self.primary_role.assign(&mut assigner)?; + let recovery_role = self.recovery_role.assign(&mut assigner)?; + let confirmation_role = self.confirmation_role.assign(&mut assigner)?; - Ok(MatrixOfFactorSourceIds { - built: PhantomData, - primary_role, - recovery_role, - confirmation_role, - number_of_days_until_auto_confirm, - }) + let matrix = unsafe { + MatrixOfFactorSourceIds::unbuilt_with_roles_and_days( + primary_role, + recovery_role, + confirmation_role, + number_of_days_until_auto_confirm, + ) + }; + + Ok(matrix) } } @@ -105,13 +112,8 @@ impl MatrixTemplate { recovery_role: RecoveryRoleTemplate, confirmation_role: ConfirmationRoleTemplate, ) -> Self { - Self { - built: PhantomData, - primary_role, - recovery_role, - confirmation_role, - number_of_days_until_auto_confirm: - MatrixOfFactorSourceIds::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM, + unsafe { + Self::unbuilt_with_roles_and_days(primary_role, recovery_role, confirmation_role, MatrixOfFactorSourceIds::DEFAULT_NUMBER_OF_DAYS_UNTIL_AUTO_CONFIRM) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs index a70826cf1..6a5b15e65 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs @@ -1,7 +1,6 @@ use crate::prelude::*; -pub type MatrixOfFactorInstances = - AbstractMatrixBuilderOrBuilt; +pub type MatrixOfFactorInstances = AbstractMatrixBuilt; pub trait HasFactorInstances { fn unique_factor_instances(&self) -> IndexSet; @@ -166,13 +165,13 @@ impl MatrixOfFactorInstances { &matrix_of_factor_sources, )?; - let matrix = Self { - built: PhantomData, - primary_role, - recovery_role, - confirmation_role, - number_of_days_until_auto_confirm: matrix_of_factor_sources - .number_of_days_until_auto_confirm, + let matrix = unsafe { + Self::unbuilt_with_roles_and_days( + primary_role, + recovery_role, + confirmation_role, + matrix_of_factor_sources.number_of_days_until_auto_confirm, + ) }; // Now that we have assigned instances, **possibly the SAME INSTANCE to multiple roles**, diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs index 99bdfb580..eb9cbf67d 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs @@ -25,14 +25,16 @@ impl MatrixOfFactorSources { unreachable!("Programmer error!") } - Ok(Self { - built: PhantomData, - primary_role, - recovery_role, - confirmation_role, - number_of_days_until_auto_confirm: matrix - .number_of_days_until_auto_confirm, - }) + let built = unsafe { + Self::unbuilt_with_roles_and_days( + primary_role, + recovery_role, + confirmation_role, + matrix.number_of_days_until_auto_confirm, + ) + }; + + Ok(built) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs index 94682efe9..b65c775d6 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -29,11 +29,7 @@ use crate::prelude::*; /// * ConfirmationRoleWithFactorInstances #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct AbstractRoleBuilderOrBuilt { - #[serde(skip)] - #[doc(hidden)] - built: PhantomData, - +pub struct AbstractRoleBuilderOrBuilt { /// How many threshold factors that must be used to perform some function with /// this role. threshold: u8, @@ -49,13 +45,13 @@ pub struct AbstractRoleBuilderOrBuilt { } pub(crate) type AbstractBuiltRoleWithFactor = - AbstractRoleBuilderOrBuilt; + AbstractRoleBuilderOrBuilt; pub(crate) type RoleBuilder = - AbstractRoleBuilderOrBuilt; + AbstractRoleBuilderOrBuilt; -impl - AbstractRoleBuilderOrBuilt +impl + AbstractRoleBuilderOrBuilt { pub fn role(&self) -> RoleKind { RoleKind::from_u8(ROLE).expect("RoleKind should be valid") @@ -97,7 +93,6 @@ impl .expect("Should not have allowed building of invalid Role"); Self { - built: PhantomData, threshold, threshold_factors, override_factors, @@ -119,8 +114,8 @@ impl } } -impl - AbstractRoleBuilderOrBuilt +impl + AbstractRoleBuilderOrBuilt { /// Threshold and Override factors mixed (threshold first). pub fn all_factors(&self) -> Vec<&FACTOR> { @@ -172,7 +167,6 @@ impl RoleFromDiscriminator for RoleKind { impl RoleBuilder { pub(crate) fn new() -> Self { Self { - built: PhantomData, threshold: 0, threshold_factors: Vec::new(), override_factors: Vec::new(), diff --git a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs index ec457b5ee..c9b3f75e1 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_structure_of_factors/security_structure_of_factor_sources.rs @@ -58,10 +58,13 @@ impl From } } -impl From> - for AbstractRoleBuilderOrBuilt +impl + From> + for AbstractRoleBuilderOrBuilt { - fn from(value: AbstractRoleBuilderOrBuilt) -> Self { + fn from( + value: AbstractRoleBuilderOrBuilt, + ) -> Self { Self::with_factors( value.get_threshold(), value From 881a80c7ce9b3f819f727aa2b77f5ea008c6550c Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 09:40:53 +0100 Subject: [PATCH 54/70] swiftformat --- .../Profile/MFA/SecurityShieldsBuilderTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index 2f0e10b21..d8d504c42 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -60,7 +60,7 @@ struct ShieldTests { let builder = SecurityShieldBuilder() #expect(builder.validationForAdditionOfFactorSourceToPrimaryOverrideForEach(factorSources: [TrustedContactFactorSource.sample.asGeneral.id]).compactMap(\.reasonIfInvalid) == [FactorSourceValidationStatusReasonIfInvalid.nonBasic(SecurityShieldBuilderInvalidReason.PrimaryCannotContainTrustedContact)]) } - + @Test("Auto lowering of threshold upon deletion") func deleteFactorSourceFromPrimaryLowersThreshold() { let builder = SecurityShieldBuilder() @@ -74,12 +74,12 @@ struct ShieldTests { builder.addFactorSourceToRecoveryOverride(factorSourceId: y) #expect(builder.recoveryRoleFactors == [y]) - + #expect(builder.threshold == 3) - + builder.removeFactorFromPrimary(factorSourceId: x) #expect(builder.threshold == 2) - + builder.removeFactorFromAllRoles(factorSourceId: y) #expect(builder.recoveryRoleFactors == []) // assert `y` is removed from Recovery and Primary #expect(builder.threshold == 1) From 87b7ab066f734ee23246cfd966ae647f0d0fa94e Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 09:57:06 +0100 Subject: [PATCH 55/70] polish --- ...curify_entity_factor_instances_provider.rs | 2 +- .../matrices/builder/matrix_builder.rs | 2 +- .../builder/matrix_builder_unit_tests.rs | 22 +++---- .../matrices/builder/matrix_template.rs | 60 +++++++++---------- .../matrices/matrix_of_factor_source_ids.rs | 30 +++++----- .../roles/builder/roles_builder.rs | 4 +- .../role_with_factor_instances.rs | 25 -------- .../roles_with_factor_sources.rs | 31 ++++++++++ 8 files changed, 91 insertions(+), 85 deletions(-) diff --git a/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs b/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs index 149eacb9d..a3c437c7b 100644 --- a/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs +++ b/crates/sargon/src/factor_instances_provider/provider/provider_adopters/securify_entity_factor_instances_provider.rs @@ -296,7 +296,7 @@ mod tests { .await .unwrap(); let factor_sources = &os.profile().unwrap().factor_sources; - let matrix_ids = MatrixTemplate::config_14() + let matrix_ids = MatrixTemplate::config_1_4() .materialize(factor_sources.items()) .unwrap(); diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index 1ac9444d7..0572fc975 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -151,7 +151,7 @@ impl MatrixBuilder { ) } - pub fn validate_each_role_in_isolation(&self) -> MatrixBuilderMutateResult { + fn validate_each_role_in_isolation(&self) -> MatrixBuilderMutateResult { self.primary_role .validate() .into_matrix_err(RoleKind::Primary)?; diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs index 5f64fd552..c74cead17 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs @@ -1343,7 +1343,7 @@ mod shield_configs { use super::*; #[test] - fn config_11() { + fn config_1_1() { let mut sut = make(); // Primary @@ -1409,7 +1409,7 @@ mod shield_configs { } #[test] - fn config_12() { + fn config_1_2() { let mut sut = make(); // Primary @@ -1469,7 +1469,7 @@ mod shield_configs { } #[test] - fn config_13() { + fn config_1_3() { let mut sut = make(); // Primary @@ -1533,7 +1533,7 @@ mod shield_configs { } #[test] - fn config_14() { + fn config_1_4() { let mut sut = make(); // Primary @@ -1582,7 +1582,7 @@ mod shield_configs { } #[test] - fn config_15() { + fn config_1_5() { let mut sut = make(); // Primary @@ -1631,7 +1631,7 @@ mod shield_configs { } #[test] - fn config_21() { + fn config_2_1() { let mut sut = make(); // Primary @@ -1692,7 +1692,7 @@ mod shield_configs { } #[test] - fn config_22() { + fn config_2_2() { let mut sut = make(); // Primary @@ -1753,7 +1753,7 @@ mod shield_configs { } #[test] - fn config_23() { + fn config_2_3() { let mut sut = make(); // Primary @@ -1802,7 +1802,7 @@ mod shield_configs { } #[test] - fn config_24() { + fn config_2_4() { let mut sut = make(); // Primary @@ -1851,7 +1851,7 @@ mod shield_configs { } #[test] - fn config_30() { + fn config_3_0() { let mut sut = make(); // Primary @@ -1917,7 +1917,7 @@ mod shield_configs { } #[test] - fn config_40() { + fn config_4_0() { let mut sut = make(); // Primary diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs index 8d76924c4..13b9a687a 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -117,7 +117,7 @@ impl MatrixTemplate { } } - pub fn config_11() -> Self { + pub fn config_1_1() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -131,7 +131,7 @@ impl MatrixTemplate { ) } - pub fn config_12() -> Self { + pub fn config_1_2() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::ledger(), @@ -145,7 +145,7 @@ impl MatrixTemplate { ) } - pub fn config_13() -> Self { + pub fn config_1_3() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -159,7 +159,7 @@ impl MatrixTemplate { ) } - pub fn config_14() -> Self { + pub fn config_1_4() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), RecoveryRoleTemplate::new([FactorSourceTemplate::ledger()]), @@ -167,7 +167,7 @@ impl MatrixTemplate { ) } - pub fn config_15() -> Self { + pub fn config_1_5() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::ledger()]), RecoveryRoleTemplate::new([FactorSourceTemplate::device()]), @@ -175,7 +175,7 @@ impl MatrixTemplate { ) } - pub fn config_21() -> Self { + pub fn config_2_1() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -189,7 +189,7 @@ impl MatrixTemplate { ) } - pub fn config_22() -> Self { + pub fn config_2_2() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::ledger(), @@ -203,7 +203,7 @@ impl MatrixTemplate { ) } - pub fn config_23() -> Self { + pub fn config_2_3() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::ledger()]), RecoveryRoleTemplate::new([FactorSourceTemplate::ledger_other()]), @@ -211,7 +211,7 @@ impl MatrixTemplate { ) } - pub fn config_24() -> Self { + pub fn config_2_4() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), RecoveryRoleTemplate::new([FactorSourceTemplate::ledger()]), @@ -221,7 +221,7 @@ impl MatrixTemplate { ) } - pub fn config_30() -> Self { + pub fn config_3_0() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -238,7 +238,7 @@ impl MatrixTemplate { ) } - pub fn config_40() -> Self { + pub fn config_4_0() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -288,7 +288,7 @@ impl MatrixTemplate { ) } - pub fn config_60() -> Self { + pub fn config_6_0() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), RecoveryRoleTemplate::new( @@ -300,7 +300,7 @@ impl MatrixTemplate { ) } - pub fn config_70() -> Self { + pub fn config_7_0() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -314,7 +314,7 @@ impl MatrixTemplate { ) } - pub fn config_80() -> Self { + pub fn config_8_0() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -330,7 +330,7 @@ impl MatrixTemplate { ) } - pub fn config_90() -> Self { + pub fn config_9_0() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -364,7 +364,7 @@ mod test_templates { #[test] fn template_config_11() { test_template( - MatrixTemplate::config_11(), + MatrixTemplate::config_1_1(), MatrixOfFactorSourceIds::sample_config_11(), ) } @@ -372,7 +372,7 @@ mod test_templates { #[test] fn template_config_12() { test_template( - MatrixTemplate::config_12(), + MatrixTemplate::config_1_2(), MatrixOfFactorSourceIds::sample_config_12(), ) } @@ -380,7 +380,7 @@ mod test_templates { #[test] fn template_config_13() { test_template( - MatrixTemplate::config_13(), + MatrixTemplate::config_1_3(), MatrixOfFactorSourceIds::sample_config_13(), ) } @@ -388,7 +388,7 @@ mod test_templates { #[test] fn template_config_14() { test_template( - MatrixTemplate::config_14(), + MatrixTemplate::config_1_4(), MatrixOfFactorSourceIds::sample_config_14(), ) } @@ -396,7 +396,7 @@ mod test_templates { #[test] fn template_config_15() { test_template( - MatrixTemplate::config_15(), + MatrixTemplate::config_1_5(), MatrixOfFactorSourceIds::sample_config_15(), ) } @@ -404,7 +404,7 @@ mod test_templates { #[test] fn template_config_21() { test_template( - MatrixTemplate::config_21(), + MatrixTemplate::config_2_1(), MatrixOfFactorSourceIds::sample_config_21(), ) } @@ -412,7 +412,7 @@ mod test_templates { #[test] fn template_config_22() { test_template( - MatrixTemplate::config_22(), + MatrixTemplate::config_2_2(), MatrixOfFactorSourceIds::sample_config_22(), ) } @@ -420,7 +420,7 @@ mod test_templates { #[test] fn template_config_23() { test_template( - MatrixTemplate::config_23(), + MatrixTemplate::config_2_3(), MatrixOfFactorSourceIds::sample_config_23(), ) } @@ -428,7 +428,7 @@ mod test_templates { #[test] fn template_config_24() { test_template( - MatrixTemplate::config_24(), + MatrixTemplate::config_2_4(), MatrixOfFactorSourceIds::sample_config_24(), ) } @@ -436,7 +436,7 @@ mod test_templates { #[test] fn template_config_30() { test_template( - MatrixTemplate::config_30(), + MatrixTemplate::config_3_0(), MatrixOfFactorSourceIds::sample_config_30(), ) } @@ -444,7 +444,7 @@ mod test_templates { #[test] fn template_config_40() { test_template( - MatrixTemplate::config_40(), + MatrixTemplate::config_4_0(), MatrixOfFactorSourceIds::sample_config_40(), ) } @@ -468,7 +468,7 @@ mod test_templates { #[test] fn template_config_60() { test_template( - MatrixTemplate::config_60(), + MatrixTemplate::config_6_0(), MatrixOfFactorSourceIds::sample_config_60(), ) } @@ -476,7 +476,7 @@ mod test_templates { #[test] fn template_config_70() { test_template( - MatrixTemplate::config_70(), + MatrixTemplate::config_7_0(), MatrixOfFactorSourceIds::sample_config_70(), ) } @@ -484,7 +484,7 @@ mod test_templates { #[test] fn template_config_80() { test_template( - MatrixTemplate::config_80(), + MatrixTemplate::config_8_0(), MatrixOfFactorSourceIds::sample_config_80(), ) } @@ -492,7 +492,7 @@ mod test_templates { #[test] fn template_config_90() { test_template( - MatrixTemplate::config_90(), + MatrixTemplate::config_9_0(), MatrixOfFactorSourceIds::sample_config_90(), ) } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index b53c73960..4d237775f 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -59,47 +59,47 @@ impl MatrixOfFactorSourceIds { } pub fn sample_config_11() -> Self { - Self::sample_from_template(MatrixTemplate::config_11()) + Self::sample_from_template(MatrixTemplate::config_1_1()) } pub fn sample_config_12() -> Self { - Self::sample_from_template(MatrixTemplate::config_12()) + Self::sample_from_template(MatrixTemplate::config_1_2()) } pub fn sample_config_13() -> Self { - Self::sample_from_template(MatrixTemplate::config_13()) + Self::sample_from_template(MatrixTemplate::config_1_3()) } pub fn sample_config_14() -> Self { - Self::sample_from_template(MatrixTemplate::config_14()) + Self::sample_from_template(MatrixTemplate::config_1_4()) } pub fn sample_config_15() -> Self { - Self::sample_from_template(MatrixTemplate::config_15()) + Self::sample_from_template(MatrixTemplate::config_1_5()) } pub fn sample_config_21() -> Self { - Self::sample_from_template(MatrixTemplate::config_21()) + Self::sample_from_template(MatrixTemplate::config_2_1()) } pub fn sample_config_22() -> Self { - Self::sample_from_template(MatrixTemplate::config_22()) + Self::sample_from_template(MatrixTemplate::config_2_2()) } pub fn sample_config_23() -> Self { - Self::sample_from_template(MatrixTemplate::config_23()) + Self::sample_from_template(MatrixTemplate::config_2_3()) } pub fn sample_config_24() -> Self { - Self::sample_from_template(MatrixTemplate::config_24()) + Self::sample_from_template(MatrixTemplate::config_2_4()) } pub fn sample_config_30() -> Self { - Self::sample_from_template(MatrixTemplate::config_30()) + Self::sample_from_template(MatrixTemplate::config_3_0()) } pub fn sample_config_40() -> Self { - Self::sample_from_template(MatrixTemplate::config_40()) + Self::sample_from_template(MatrixTemplate::config_4_0()) } pub fn sample_config_51() -> Self { @@ -111,19 +111,19 @@ impl MatrixOfFactorSourceIds { } pub fn sample_config_60() -> Self { - Self::sample_from_template(MatrixTemplate::config_60()) + Self::sample_from_template(MatrixTemplate::config_6_0()) } pub fn sample_config_70() -> Self { - Self::sample_from_template(MatrixTemplate::config_70()) + Self::sample_from_template(MatrixTemplate::config_7_0()) } pub fn sample_config_80() -> Self { - Self::sample_from_template(MatrixTemplate::config_80()) + Self::sample_from_template(MatrixTemplate::config_8_0()) } pub fn sample_config_90() -> Self { - Self::sample_from_template(MatrixTemplate::config_90()) + Self::sample_from_template(MatrixTemplate::config_9_0()) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 26a27e903..2dd99f155 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -227,7 +227,7 @@ where self._add_factor_source_to_list(factor_source_id, Threshold) } - /// If we would add a factor of kind `factor_source_kind` to the list of kind `factor_list_kind` + /// If we would add a factor of kind `factor_source_kind` to the list of kind `Threshold` /// what would be the validation status? pub(crate) fn validation_for_addition_of_factor_source_of_kind_to_threshold( &self, @@ -299,7 +299,7 @@ impl RoleBuilder { validation } - /// If we would add a factor of kind `factor_source_kind` to the list of kind `factor_list_kind` + /// If we would add a factor of kind `factor_source_kind` to the list of kind `Override` /// what would be the validation status? pub(crate) fn validation_for_addition_of_factor_source_of_kind_to_override( &self, diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs index d8c8e5521..de09125b9 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs @@ -3,32 +3,7 @@ use crate::prelude::*; pub(crate) type RoleWithFactorInstances = AbstractBuiltRoleWithFactor; -impl RoleWithFactorSources { - fn from( - other: &RoleWithFactorSources, - ) -> Self { - Self::with_factors( - other.get_threshold(), - other.get_threshold_factors().clone(), - other.get_override_factors().clone(), - ) - } -} -impl MatrixOfFactorSources { - pub(crate) fn get_role( - &self, - ) -> RoleWithFactorSources { - match ROLE { - ROLE_PRIMARY => RoleWithFactorSources::from(&self.primary_role), - ROLE_RECOVERY => RoleWithFactorSources::from(&self.recovery_role), - ROLE_CONFIRMATION => { - RoleWithFactorSources::from(&self.confirmation_role) - } - _ => panic!("unknown"), - } - } -} impl RoleWithFactorInstances { pub(crate) fn fulfilling_role_of_factor_sources_with_factor_instances( diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs index 0a664f3b5..92810fa69 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs @@ -3,6 +3,37 @@ use crate::prelude::*; pub(crate) type RoleWithFactorSources = AbstractBuiltRoleWithFactor; +impl RoleWithFactorSources { + fn from( + other: &RoleWithFactorSources, + ) -> Self { + Self::with_factors( + other.get_threshold(), + other.get_threshold_factors().clone(), + other.get_override_factors().clone(), + ) + } +} + +impl MatrixOfFactorSources { + pub(crate) fn get_role( + &self, + ) -> RoleWithFactorSources { + match ROLE { + ROLE_PRIMARY => { + RoleWithFactorSources::::from(&self.primary_role) + } + ROLE_RECOVERY => { + RoleWithFactorSources::::from(&self.recovery_role) + } + ROLE_CONFIRMATION => { + RoleWithFactorSources::::from(&self.confirmation_role) + } + _ => panic!("unknown"), + } + } +} + impl RoleWithFactorSources { pub fn new( role_with_factor_source_ids: RoleWithFactorSourceIds, From ec2e65c1ea8cd964343d30a212dda9e1feaf3a15 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 09:59:51 +0100 Subject: [PATCH 56/70] fmt --- .../factor_instance_level/role_with_factor_instances.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs index de09125b9..fb3f872c1 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs @@ -3,8 +3,6 @@ use crate::prelude::*; pub(crate) type RoleWithFactorInstances = AbstractBuiltRoleWithFactor; - - impl RoleWithFactorInstances { pub(crate) fn fulfilling_role_of_factor_sources_with_factor_instances( consuming_instances: &IndexMap, From 810d35dedb858074c1986923ec0ae293db6ee170 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 10:05:19 +0100 Subject: [PATCH 57/70] doc --- .../matrices/builder/matrix_template.rs | 51 +++++++++++++++++++ .../matrices/matrix_of_factor_source_ids.rs | 51 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs index 13b9a687a..c804b55cf 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -117,6 +117,9 @@ impl MatrixTemplate { } } + /// Config 1.1 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_1_1() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -131,6 +134,9 @@ impl MatrixTemplate { ) } + /// Config 1.2 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_1_2() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -145,6 +151,9 @@ impl MatrixTemplate { ) } + /// Config 1.3 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_1_3() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -159,6 +168,9 @@ impl MatrixTemplate { ) } + /// Config 1.4 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_1_4() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), @@ -167,6 +179,9 @@ impl MatrixTemplate { ) } + /// Config 1.5 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_1_5() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::ledger()]), @@ -175,6 +190,9 @@ impl MatrixTemplate { ) } + /// Config 2.1 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_2_1() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -189,6 +207,9 @@ impl MatrixTemplate { ) } + /// Config 2.2 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_2_2() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -203,6 +224,9 @@ impl MatrixTemplate { ) } + /// Config 2.3 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_2_3() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::ledger()]), @@ -211,6 +235,9 @@ impl MatrixTemplate { ) } + /// Config 2.4 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_2_4() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), @@ -221,6 +248,9 @@ impl MatrixTemplate { ) } + /// Config 3 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_3_0() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -238,6 +268,9 @@ impl MatrixTemplate { ) } + /// Config 4 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_4_0() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -256,6 +289,9 @@ impl MatrixTemplate { ) } + /// Config 5.1 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_51() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -269,6 +305,9 @@ impl MatrixTemplate { ) } + /// Config 5.2 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_52() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -288,6 +327,9 @@ impl MatrixTemplate { ) } + /// Config 6.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_6_0() -> Self { Self::new( PrimaryRoleTemplate::new([FactorSourceTemplate::device()]), @@ -300,6 +342,9 @@ impl MatrixTemplate { ) } + /// Config 7.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_7_0() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -314,6 +359,9 @@ impl MatrixTemplate { ) } + /// Config 8.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_8_0() -> Self { Self::new( PrimaryRoleTemplate::new([ @@ -330,6 +378,9 @@ impl MatrixTemplate { ) } + /// Config 9.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn config_9_0() -> Self { Self::new( PrimaryRoleTemplate::new([ diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 4d237775f..ccec1325c 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -58,70 +58,121 @@ impl MatrixOfFactorSourceIds { .unwrap() } + /// Config 1.1 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_11() -> Self { Self::sample_from_template(MatrixTemplate::config_1_1()) } + /// Config 1.2 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_12() -> Self { Self::sample_from_template(MatrixTemplate::config_1_2()) } + /// Config 1.3 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_13() -> Self { Self::sample_from_template(MatrixTemplate::config_1_3()) } + /// Config 1.4 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_14() -> Self { Self::sample_from_template(MatrixTemplate::config_1_4()) } + /// Config 1.5 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_15() -> Self { Self::sample_from_template(MatrixTemplate::config_1_5()) } + /// Config 2.1 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_21() -> Self { Self::sample_from_template(MatrixTemplate::config_2_1()) } + /// Config 2.2 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_22() -> Self { Self::sample_from_template(MatrixTemplate::config_2_2()) } + /// Config 2.3 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_23() -> Self { Self::sample_from_template(MatrixTemplate::config_2_3()) } + /// Config 2.4 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_24() -> Self { Self::sample_from_template(MatrixTemplate::config_2_4()) } + /// Config 3.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_30() -> Self { Self::sample_from_template(MatrixTemplate::config_3_0()) } + /// Config 4.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_40() -> Self { Self::sample_from_template(MatrixTemplate::config_4_0()) } + /// Config 5.1 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_51() -> Self { Self::sample_from_template(MatrixTemplate::config_51()) } + /// Config 5.2 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_52() -> Self { Self::sample_from_template(MatrixTemplate::config_52()) } + /// Config 6.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_60() -> Self { Self::sample_from_template(MatrixTemplate::config_6_0()) } + /// Config 7.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_70() -> Self { Self::sample_from_template(MatrixTemplate::config_7_0()) } + /// Config 8.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_80() -> Self { Self::sample_from_template(MatrixTemplate::config_8_0()) } + /// Config 9.0 according to [this document][doc]. + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations pub fn sample_config_90() -> Self { Self::sample_from_template(MatrixTemplate::config_9_0()) } From 65f473caa6dfc680ebc120dba82a0b34a0d0b45f Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 10:37:57 +0100 Subject: [PATCH 58/70] polish --- .../roles/builder/confirmation_roles_builder_unit_tests.rs | 5 ----- .../roles/builder/primary_roles_builder_unit_tests.rs | 7 ++++--- .../roles/builder/recovery_roles_builder_unit_tests.rs | 5 ----- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs index 82c8bf196..a7b9bd2cd 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/confirmation_roles_builder_unit_tests.rs @@ -93,7 +93,6 @@ mod device_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -142,7 +141,6 @@ mod ledger_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -189,7 +187,6 @@ mod arculus_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -236,7 +233,6 @@ mod off_device_mnemonic_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -330,7 +326,6 @@ mod password_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs index 047cd21a8..0ffb0166c 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs @@ -174,11 +174,12 @@ mod threshold_suite { FactorSourceID::sample_arculus_other(), ) .unwrap(); - sut.set_threshold(3).unwrap(); - assert_eq!(sut.get_threshold(), 3); + sut.set_threshold(2).unwrap(); + assert_eq!(sut.get_threshold(), 2); sut.remove_factor_source(&fs0).unwrap(); + assert_eq!(sut.get_threshold(), 2); // assert that we DIDN'T lower the threshold, since we have 2 factors sut.remove_factor_source(&fs1).unwrap(); - assert_eq!(sut.get_threshold(), 1); + assert_eq!(sut.get_threshold(), 1); // assert that we DID lower the threshold now that we have 1 factor } #[test] diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs index 9a238b295..8a27eec1a 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/recovery_roles_builder_unit_tests.rs @@ -91,7 +91,6 @@ mod device_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -160,7 +159,6 @@ mod ledger_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -207,7 +205,6 @@ mod arculus_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -254,7 +251,6 @@ mod off_device_mnemonic_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); @@ -301,7 +297,6 @@ mod trusted_contact_in_isolation { #[test] fn two_of_same_kind_allowed() { - // TODO: Ask Matt // Arrange let mut sut = make(); From c686653a0535878fce5794b0e14313cd2e587291 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 13:25:25 +0100 Subject: [PATCH 59/70] Automatically bump threshold on primary role if we add a factorsource to threshold and threshold was 0. add doc support for macros. doc types. --- .../MFA/SecurityShieldsBuilderTests.swift | 22 +++++-- .../matrices/decl_matrix_macro.rs | 24 +++++++- .../matrices/matrix_of_factor_instances.rs | 5 +- .../matrices/matrix_of_factor_source_ids.rs | 5 +- .../matrices/matrix_of_factor_sources.rs | 5 +- ...ource_in_role_builder_validation_status.rs | 4 ++ .../security_structures/models/role_kind.rs | 1 + .../roles/decl_role_macro.rs | 57 ++++++++++++++++--- .../roles/roles_factor_instances.rs | 5 +- .../roles/roles_factor_source_ids.rs | 5 +- .../roles/roles_factor_sources.rs | 5 +- .../security_shield_builder.rs | 23 +++++++- .../security_shield_builder_invalid_reason.rs | 2 +- .../security_structure_of_factor_instances.rs | 23 +------- ...security_structure_of_factor_source_ids.rs | 20 +++++++ .../matrices/builder/matrix_builder.rs | 4 +- .../builder/matrix_builder_unit_tests.rs | 4 ++ .../primary_roles_builder_unit_tests.rs | 15 +++-- .../roles/builder/roles_builder.rs | 26 +++++++-- .../security_shield_builder.rs | 21 ++++++- .../security_shield_builder_invalid_reason.rs | 6 +- 21 files changed, 218 insertions(+), 64 deletions(-) diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index d8d504c42..ace0dd9ee 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -89,8 +89,21 @@ struct ShieldTests { #expect(builder.primaryRoleThresholdFactors == []) } - @Test("Complete") - func complete() throws { + @Test("Validate") + func validate() throws { + let builder = SecurityShieldBuilder() + #expect(builder.validate() == .PrimaryRoleMustHaveAtLeastOneFactor) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) + #expect(builder.primaryRoleThresholdFactors == [.sampleDevice]) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDeviceOther) + + #expect(builder.validate() == .RecoveryRoleMustHaveAtLeastOneFactor) + builder.removeFactorFromPrimary(factorSourceId: .sampleDeviceOther) + } + + @Test("Build") + func build() throws { let builder = SecurityShieldBuilder() builder.setName(name: "S.H.I.E.L.D.") builder.numberOfDaysUntilAutoConfirm = 42 @@ -98,8 +111,9 @@ struct ShieldTests { #expect(builder.validate() == .PrimaryRoleMustHaveAtLeastOneFactor) // Primary - builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) - builder.threshold = 1 + #expect(builder.threshold == 0) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) // bumps threshold + #expect(builder.threshold == 1) builder.addFactorSourceToPrimaryOverride(factorSourceId: .sampleArculus) builder.addFactorSourceToPrimaryOverride(factorSourceId: .sampleArculusOther) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs index 65ca4e634..f5753981f 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs @@ -67,8 +67,17 @@ macro_rules! matrix_conversion { } } }; - (struct: $struct_name:ident, $factor_level:ty) => { + ( + struct: + $( + #[doc = $expr: expr] + )* + $struct_name:ident, $factor_level:ty + ) => { paste! { + $( + #[doc = $expr] + )* #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] pub struct $struct_name { pub primary_role: [< PrimaryRoleWith $factor_level s >], @@ -85,11 +94,20 @@ macro_rules! matrix_conversion { ); } }; - ($factor_level:ty) => { + ( + $( + #[doc = $expr: expr] + )* + $factor_level:ty + ) => { paste! { use sargon::$factor_level as [< Internal $factor_level>]; matrix_conversion!( - struct: [< MatrixOf $factor_level s >], + struct: + $( + #[doc = $expr] + )* + [< MatrixOf $factor_level s >], $factor_level ); } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs index e03790b19..454582a1d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_instances.rs @@ -2,4 +2,7 @@ use crate::prelude::*; use super::decl_matrix_macro::matrix_conversion; -matrix_conversion!(FactorInstance); +matrix_conversion!( + /// Matrix of `FactorInstance`s containing the primary, recovery, and confirmation roles with `FactorInstance`s + FactorInstance +); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 52ab714fb..6fcee16a5 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -2,7 +2,10 @@ use crate::prelude::*; use super::decl_matrix_macro::matrix_conversion; -matrix_conversion!(FactorSourceID); +matrix_conversion!( + /// Matrix of `FactorSourceID`s containing the primary, recovery, and confirmation roles with `FactorSourceID`s + FactorSourceID +); macro_rules! export_sample_config { ($config_ident:literal) => { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs index 9a2afc118..94f3f52b0 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_sources.rs @@ -2,7 +2,10 @@ use crate::prelude::*; use super::decl_matrix_macro::matrix_conversion; -matrix_conversion!(FactorSource); +matrix_conversion!( + /// Matrix of `FactorSource`s containing the primary, recovery, and confirmation roles with `FactorSource`s + FactorSource +); #[cfg(test)] mod tests { diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs index f386f65ad..f00dabf0f 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/factor_source_in_role_builder_validation_status.rs @@ -2,6 +2,8 @@ use sargon::AsShieldBuilderViolation; use crate::prelude::*; +/// The "validation result" of a `FactorSourceID` in a `Role`, if +/// it we were to add it to a role list. #[derive(Clone, Debug, PartialEq, uniffi::Record)] pub struct FactorSourceValidationStatus { pub role: RoleKind, @@ -9,6 +11,8 @@ pub struct FactorSourceValidationStatus { pub reason_if_invalid: Option, } +/// The reason why a `FactorSourceID` is invalid if it were +/// to be added into a factor list for some role. #[derive(Clone, Debug, PartialEq, uniffi::Enum)] pub enum FactorSourceValidationStatusReasonIfInvalid { BasicViolation(String), diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs index 8560dc72f..d2d8f2bc0 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/models/role_kind.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use sargon::RoleKind as InternalRoleKind; +/// A discriminator of a role in a matrix of Factors. #[derive( Clone, Copy, Debug, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum, )] diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs index 43cc63078..f27547b08 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/decl_role_macro.rs @@ -25,13 +25,36 @@ use crate::prelude::*; // Furthermore it will generate `HasSampleValues` impl for each of the generated structs. // and also uniffi export them. macro_rules! role_conversion { - ($factor_level:ty) => { + ( + $( + #[doc = $expr: expr] + )* + $factor_level:ty + ) => { paste! { use sargon::$factor_level as [< Internal $factor_level>]; } - role_conversion_inner!(for: Primary $factor_level); - role_conversion_inner!(for: Recovery $factor_level); - role_conversion_inner!(for: Confirmation $factor_level); + role_conversion_inner!( + for: + $( + #[doc = $expr] + )* + Primary $factor_level + ); + role_conversion_inner!( + for: + $( + #[doc = $expr] + )* + Recovery $factor_level) + ; + role_conversion_inner!( + for: + $( + #[doc = $expr] + )* + Confirmation $factor_level + ); }; } @@ -114,8 +137,16 @@ macro_rules! role_conversion_inner { // Declare the struct and impl `From // and by recursively calling `role_conversion_inner` also impl // `From` conversions. - (struct: $struct_name:ident, $factor_level:ty) => { - /// A role with a threshold, threshold_factors and override_factors. + ( + struct: + $( + #[doc = $expr: expr] + )* + $struct_name:ident, $factor_level:ty + ) => { + $( + #[doc = $expr] + )* #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] pub struct $struct_name { @@ -140,10 +171,20 @@ macro_rules! role_conversion_inner { ); } }; - (for: $role:ident $factor_level:ty) => { + ( + for: + $( + #[doc = $expr: expr] + )* + $role:ident $factor_level:ty + ) => { paste! { role_conversion_inner!( - struct: [< $role RoleWith $factor_level s >], + struct: + $( + #[doc = $expr] + )* + [< $role RoleWith $factor_level s >], $factor_level ); } diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs index 0eacbffe6..7ffd948ce 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_instances.rs @@ -2,4 +2,7 @@ use crate::prelude::*; use super::decl_role_macro::role_conversion; -role_conversion!(FactorInstance); +role_conversion!( + /// Role of FactorInstances + FactorInstance +); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs index 6968d506d..6b2c7a469 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_source_ids.rs @@ -2,4 +2,7 @@ use crate::prelude::*; use super::decl_role_macro::role_conversion; -role_conversion!(FactorSourceID); +role_conversion!( + /// Role of FactorSourceIDs + FactorSourceID +); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs index 96ce677ac..0cf74bce3 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/roles/roles_factor_sources.rs @@ -2,4 +2,7 @@ use crate::prelude::*; use super::decl_role_macro::role_conversion; -role_conversion!(FactorSource); +role_conversion!( + /// Role of `FactorSource`s + FactorSource +); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 1869c6db3..8331a533b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -178,6 +178,9 @@ impl SecurityShieldBuilder { } /// Adds the factor source to the primary role threshold list. + /// + /// Also sets the threshold to 1 this is the first factor set and if + /// the threshold was 0. pub fn add_factor_source_to_primary_threshold( &self, factor_source_id: FactorSourceID, @@ -418,6 +421,14 @@ impl FactorSourceID { pub fn sample_arculus_other() -> Self { Self::new(sargon::FactorSourceID::sample_arculus_other()) } + + pub fn sample_password() -> Self { + Self::new(sargon::FactorSourceID::sample_password()) + } + + pub fn sample_password_other() -> Self { + Self::new(sargon::FactorSourceID::sample_password_other()) + } } #[cfg(test)] @@ -460,14 +471,22 @@ mod tests { ); sut.add_factor_source_to_primary_threshold( + // should also bump threshold to 1 FactorSourceID::sample_device(), ); + assert_eq!(sut.get_primary_threshold(), 1); + + sut.add_factor_source_to_primary_threshold( + // should NOT bump threshold + FactorSourceID::sample_password_other(), + ); + assert_eq!(sut.get_primary_threshold(), 1); + sut.remove_factor_from_primary(FactorSourceID::sample_password_other()); + assert_eq!( sut.get_primary_threshold_factors(), vec![FactorSourceID::sample_device()] ); - sut.set_threshold(1); - assert_eq!(sut.get_primary_threshold(), 1); sut.add_factor_source_to_primary_override( FactorSourceID::sample_arculus(), ); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index ef7ee1b95..16a0a488b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -40,7 +40,7 @@ pub enum SecurityShieldBuilderInvalidReason { #[error( "Primary role with threshold factors cannot have a threshold of zero" )] - PrimaryRoleWithThresholdCannotBeZeroWithFactors, + PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero, #[error("Primary role with password in threshold list must have threshold greater than one")] PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs index ae7ccf3d8..8563e0846 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_instances.rs @@ -1,6 +1,9 @@ use crate::prelude::*; use sargon::SecurityStructureOfFactorInstances as InternalSecurityStructureOfFactorInstances; +/// A MatrixOfFactorInstances and an ID which identifies it, this is +/// the Profile data structure representation of the owner key hashes which +/// have been uploaded as Scrypto AccessRules on the AccessController on-ledger. #[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] pub struct SecurityStructureOfFactorInstances { /// The ID of the `SecurityStructureOfFactorSourceIDs` in @@ -13,23 +16,3 @@ pub struct SecurityStructureOfFactorInstances { /// and Confirmation role. pub matrix_of_factors: MatrixOfFactorInstances, } - -use sargon::SecurityStructureOfFactorSourceIDs as InternalSecurityStructureOfFactorSourceIDs; - -#[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] -pub struct SecurityStructureOfFactorSourceIDs { - /// Metadata of this Security Structure, such as globally unique and - /// stable identifier, creation date and user chosen label (name). - pub metadata: SecurityStructureMetadata, - - /// The structure of factors to use for certain roles, Primary, Recovery - /// and Confirmation role. - pub matrix_of_factors: MatrixOfFactorSourceIDs, -} - -delegate_debug_into!( - SecurityStructureOfFactorSourceIDs, - InternalSecurityStructureOfFactorSourceIDs -); - -pub type MatrixOfFactorSourceIds = MatrixOfFactorSourceIDs; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs index 2b4e95f94..8ece8ba6f 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_structures/security_structure_of_factor_source_ids.rs @@ -2,6 +2,26 @@ use crate::prelude::*; use sargon::SecurityStructureOfFactorSourceIDs as InternalSecurityStructureOfFactorSourceIDs; +pub type MatrixOfFactorSourceIds = MatrixOfFactorSourceIDs; + +/// A `MatrixOfFactorSourceIDs` and associated metadata, this is +/// the Profile data structure representation of a "SecurityShield". +#[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] +pub struct SecurityStructureOfFactorSourceIDs { + /// Metadata of this Security Structure, such as globally unique and + /// stable identifier, creation date and user chosen label (name). + pub metadata: SecurityStructureMetadata, + + /// The structure of factors to use for certain roles, Primary, Recovery + /// and Confirmation role. + pub matrix_of_factors: MatrixOfFactorSourceIDs, +} + +delegate_debug_into!( + SecurityStructureOfFactorSourceIDs, + InternalSecurityStructureOfFactorSourceIDs +); + decl_vec_samples_for!( SecurityStructuresOfFactorSourceIDs, SecurityStructureOfFactorSourceIDs diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index 0572fc975..b0df06de6 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -171,6 +171,9 @@ impl MatrixBuilder { } /// Adds the factor source to the primary role threshold list. + /// + /// Also sets the threshold to 1 this is the first factor set and if + /// the threshold was 0. pub fn add_factor_source_to_primary_threshold( &mut self, factor_source_id: FactorSourceID, @@ -303,7 +306,6 @@ impl MatrixBuilder { let r0 = self.remove_factor_from_primary(fsid); let r1 = self.remove_factor_from_recovery(fsid); let r2 = self.remove_factor_from_confirmation(fsid); - r0.or(r1).or(r2) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs index c74cead17..e40c629e4 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs @@ -401,6 +401,10 @@ mod remove { FactorSourceID::sample_device(), ) .unwrap(); + assert_eq!( + sut.primary_role.get_threshold_factors(), + &[FactorSourceID::sample_device()] + ); let res = sut.remove_factor_from_all_roles(&FactorSourceID::sample_device()); assert_eq!(res, Ok(())); diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs index 0ffb0166c..012ea265c 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/primary_roles_builder_unit_tests.rs @@ -153,13 +153,10 @@ mod threshold_suite { fn remove_lowers_threshold_from_1_to_0() { let mut sut = make(); let fs = sample(); - sut.add_factor_source_to_threshold(fs).unwrap(); - sut.set_threshold(1).unwrap(); + assert_eq!(sut.get_threshold(), 0); + sut.add_factor_source_to_threshold(fs).unwrap(); // should automatically increase threshold to 1 assert_eq!(sut.get_threshold(), 1); - assert_eq!( - sut.remove_factor_source(&fs), - Err(Validation::NotYetValid(RoleMustHaveAtLeastOneFactor)) - ); + sut.remove_factor_source(&fs).unwrap(); assert_eq!(sut.get_threshold(), 0); } @@ -668,13 +665,15 @@ mod ledger { let mut sut = make(); // Act - sut.add_factor_source_to_threshold(sample()).unwrap(); + sut.add_factor_source_to_threshold(sample()).unwrap(); // should automatically bump threshold to 1 + + let _ = sut.set_threshold(0); // Assert assert_eq!( sut.build(), Err(RoleBuilderValidation::NotYetValid( - NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors + NotYetValidReason::PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero )) ); } diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs index 2dd99f155..6a9fde824 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/builder/roles_builder.rs @@ -74,7 +74,7 @@ pub enum NotYetValidReason { #[error( "Primary role with threshold factors cannot have a threshold of zero" )] - PrimaryRoleWithThresholdCannotBeZeroWithFactors, + PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero, #[error("Primary role with password in threshold list must have threshold greater than one")] PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, @@ -218,13 +218,25 @@ impl RoleBuilder where Assert<{ ROLE == ROLE_PRIMARY }>: IsTrue, { + /// ```ignore /// If Ok | Err(NotYetValid) => self is mutated - /// If Err(ForeverInvalid) => self is not mutated + /// If Err(ForeverInvalid) => self is NOT mutated + /// ``` + /// + /// Also sets the threshold to 1 this is the first factor set and if + /// the threshold was 0. pub(crate) fn add_factor_source_to_threshold( &mut self, factor_source_id: FactorSourceID, ) -> RoleBuilderMutateResult { + let should_set_threshold_to_one = self.get_threshold() == 0 + && self.get_threshold_factors().is_empty(); self._add_factor_source_to_list(factor_source_id, Threshold) + .inspect(|_| { + if should_set_threshold_to_one { + let _ = self.set_threshold(1); + } + }) } /// If we would add a factor of kind `factor_source_kind` to the list of kind `Threshold` @@ -412,7 +424,7 @@ impl RoleBuilder { if self.get_threshold() == 0 && !self.get_threshold_factors().is_empty() { return Some( - NotYetValidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors, + NotYetValidReason::PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero, ); } None @@ -579,6 +591,7 @@ impl RoleBuilder { FactorSourceNotFound, ); } + let remove = |xs: &mut Vec| { let index = xs .iter() @@ -592,15 +605,16 @@ impl RoleBuilder { } else if self.threshold_contains_factor_source(factor_source_id) { // We use `else if` to highlight the fact that a factor cannot // ever be in both override and threshold list. - remove(self.mut_threshold_factors()); let threshold_factors_len = self.get_threshold_factors().len() as u8; if self.get_threshold() > threshold_factors_len { - self.set_threshold(threshold_factors_len)?; + // N.B. we don't use `set_threshold` since this might be a + // temporary invalid state, if e.g. primary role does not have + // any factors. + self.unchecked_set_threshold(threshold_factors_len); } } - Ok(()) } diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 0a6bae166..7de5d2257 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -115,6 +115,9 @@ impl SecurityShieldBuilder { } /// Adds the factor source to the primary role threshold list. + /// + /// Also sets the threshold to 1 this is the first factor set and if + /// the threshold was 0. pub fn add_factor_source_to_primary_threshold( &self, factor_source_id: FactorSourceID, @@ -458,6 +461,16 @@ mod tests { #[allow(clippy::upper_case_acronyms)] type SUT = SecurityShieldBuilder; + #[test] + fn add_factor_to_primary_threshold_does_not_change_already_set_threshold() { + let sut = SUT::new(); + sut.set_threshold(42); + sut.add_factor_source_to_primary_threshold( + FactorSourceID::sample_device(), + ); + assert_eq!(sut.get_threshold(), 42); + } + #[test] fn test() { let sut = SUT::default(); @@ -467,9 +480,9 @@ mod tests { // Primary .set_number_of_days_until_auto_confirm(42) .add_factor_source_to_primary_threshold( + // also sets threshold -> 1 FactorSourceID::sample_device(), ) - .set_threshold(1) .add_factor_source_to_primary_override( FactorSourceID::sample_arculus(), ) @@ -499,6 +512,7 @@ mod tests { shield.matrix_of_factors.primary().get_override_factors(), &vec![FactorSourceID::sample_arculus()] ); + assert_eq!(shield.matrix_of_factors.primary().get_threshold(), 1); assert_eq!( shield.matrix_of_factors.recovery().get_override_factors(), &vec![FactorSourceID::sample_ledger()] @@ -726,11 +740,14 @@ mod test_invalid { fn primary_role_with_threshold_cannot_be_zero_with_factors() { let sut = SUT::new(); sut.add_factor_source_to_primary_threshold( + // bumped threshold FactorSourceID::sample_device(), ); + assert_eq!(sut.get_threshold(), 1); + sut.set_threshold(0); assert_eq!( sut.validate().unwrap(), - SecurityShieldBuilderInvalidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors + SecurityShieldBuilderInvalidReason::PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero ); } diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs index 7ee6b3335..728f3ae0a 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder_invalid_reason.rs @@ -160,8 +160,8 @@ impl AsShieldBuilderViolation for (RoleKind, NotYetValidReason) { PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor => { SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor } - PrimaryRoleWithThresholdCannotBeZeroWithFactors => { - SecurityShieldBuilderInvalidReason::PrimaryRoleWithThresholdCannotBeZeroWithFactors + PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero => { + SecurityShieldBuilderInvalidReason::PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero } PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne => { SecurityShieldBuilderInvalidReason::PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne @@ -211,7 +211,7 @@ pub enum SecurityShieldBuilderInvalidReason { #[error( "Primary role with threshold factors cannot have a threshold of zero" )] - PrimaryRoleWithThresholdCannotBeZeroWithFactors, + PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero, #[error("Primary role with password in threshold list must have threshold greater than one")] PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne, From e002aedac348ab276d4b5bf5e2fd382aa86c8766 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 14:00:46 +0100 Subject: [PATCH 60/70] more swift tests --- .../MFA/SecurityShieldsBuilderTests.swift | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift index ace0dd9ee..f5cc6f042 100644 --- a/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift +++ b/apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift @@ -89,17 +89,95 @@ struct ShieldTests { #expect(builder.primaryRoleThresholdFactors == []) } - @Test("Validate") - func validate() throws { + @Test("basic validation") + func basicValidation() throws { let builder = SecurityShieldBuilder() #expect(builder.validate() == .PrimaryRoleMustHaveAtLeastOneFactor) builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) - builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) // did not get added, duplicates are not allowed #expect(builder.primaryRoleThresholdFactors == [.sampleDevice]) builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDeviceOther) #expect(builder.validate() == .RecoveryRoleMustHaveAtLeastOneFactor) builder.removeFactorFromPrimary(factorSourceId: .sampleDeviceOther) + builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleLedger) + + #expect(builder.validate() == .ConfirmationRoleMustHaveAtLeastOneFactor) + builder.addFactorSourceToConfirmationOverride(factorSourceId: .sampleArculus) + #expect(builder.validate() == nil) + #expect((try? builder.build()) != nil) + } + + @Test("primary role with threshold factors cannot have a threshold value of zero") + func primaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero() throws { + let builder = SecurityShieldBuilder() + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleLedger) + builder.threshold = 0 + #expect(builder.validate() == .PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero) + } + + @Test("cannot add forbidden FactorSourceKinds") + func preventAddOfForbiddenFactorSourceKinds() throws { + let builder = SecurityShieldBuilder() + + // Primary + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleTrustedContact) // Verboten + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleSecurityQuestions) // Verboten + + // Recovery + builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleSecurityQuestions) // Verboten + builder.addFactorSourceToRecoveryOverride(factorSourceId: .samplePassword) // Verboten + + // Confirmation + builder.addFactorSourceToConfirmationOverride(factorSourceId: .sampleTrustedContact) // Verboten + + #expect(builder.primaryRoleThresholdFactors.isEmpty) + #expect(builder.recoveryRoleFactors.isEmpty) + #expect(builder.confirmationRoleFactors.isEmpty) + } + + @Test("Primary can only contain one DeviceFactorSource") + func primaryCanOnlyContainOneDeviceFactorSourceThreshold() throws { + let builder = SecurityShieldBuilder() + let factor = FactorSourceId.sampleDevice + let other = FactorSourceId.sampleDeviceOther + builder.addFactorSourceToPrimaryThreshold(factorSourceId: factor) + builder.addFactorSourceToPrimaryOverride(factorSourceId: other) + #expect(builder.primaryRoleThresholdFactors == [factor]) + #expect(builder.primaryRoleOverrideFactors == []) + + builder.removeFactorFromPrimary(factorSourceId: factor) + + builder.addFactorSourceToPrimaryOverride(factorSourceId: factor) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: other) + #expect(builder.primaryRoleThresholdFactors == []) + #expect(builder.primaryRoleOverrideFactors == [factor]) + } + + @Test("Primary password never alone") + func primaryPasswordNeverAlone() { + let builder = SecurityShieldBuilder() + builder.addFactorSourceToPrimaryOverride(factorSourceId: .samplePassword) // not allowed + #expect(builder.primaryRoleOverrideFactors.isEmpty) + + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .samplePassword) + #expect(builder.validate() == .PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero) + builder.threshold = 0 + #expect(builder.validate() == .PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero) + builder.threshold = 1 + #expect(builder.validate() == .PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor) + builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleLedger) + #expect(builder.validate() == .PrimaryRoleWithPasswordInThresholdListMustThresholdGreaterThanOne) + builder.threshold = 2 + + builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleArculus) + builder.addFactorSourceToConfirmationOverride(factorSourceId: .sampleArculusOther) + + let shield = try! builder.build() + + #expect(shield.matrixOfFactors.primaryRole.overrideFactors.isEmpty) + #expect(shield.matrixOfFactors.primaryRole.threshold == 2) + #expect(shield.matrixOfFactors.primaryRole.thresholdFactors == [.samplePassword, .sampleLedger]) } @Test("Build") From 7c1f671aa10132326dca0217f5ffb6850cd69877 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 15:26:07 +0100 Subject: [PATCH 61/70] Add reset_primary_and_confirmation_role_state to Uniffi ShieldBuilder. --- .../security_shield_builder.rs | 23 +++++++++++++++++++ .../matrices/builder/matrix_builder.rs | 5 ++++ .../roles/abstract_role_builder_or_built.rs | 14 +++++++++++ .../security_shield_builder.rs | 6 +++++ 4 files changed, 48 insertions(+) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 8331a533b..67c3492d8 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -224,6 +224,12 @@ impl SecurityShieldBuilder { ) }) } + + pub fn reset_recovery_and_confirmation_role_state(&self) { + self.set(|builder| { + builder.reset_recovery_and_confirmation_role_state() + }); + } } #[uniffi::export] @@ -521,6 +527,23 @@ mod tests { FactorSourceID::sample_ledger_other(), ); + assert_eq!( + sut.get_recovery_factors(), + vec![ + FactorSourceID::sample_ledger(), + FactorSourceID::sample_ledger_other() + ] + ); + sut.reset_recovery_and_confirmation_role_state(); + assert_eq!(sut.get_recovery_factors(), vec![]); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ); + assert_eq!( sut.get_recovery_factors(), vec![ diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs index b0df06de6..1423eaf25 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder.rs @@ -183,6 +183,11 @@ impl MatrixBuilder { .into_matrix_err(RoleKind::Primary) } + pub fn reset_recovery_and_confirmation_role_state(&mut self) { + self.recovery_role.reset(); + self.confirmation_role.reset(); + } + /// Adds the factor source to the primary role override list. pub fn add_factor_source_to_primary_override( &mut self, diff --git a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs index b65c775d6..edd52ad74 100644 --- a/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs +++ b/crates/sargon/src/profile/mfa/security_structures/roles/abstract_role_builder_or_built.rs @@ -44,6 +44,20 @@ pub struct AbstractRoleBuilderOrBuilt { override_factors: Vec, } +impl RoleBuilder +where + Assert<{ ROLE > ROLE_PRIMARY }>: IsTrue, +{ + /// Removes all override factors from this role + pub fn reset(&mut self) { + self.override_factors.clear(); + + // This is not necessary, but why not... + self.threshold_factors.clear(); + self.threshold = 0; + } +} + pub(crate) type AbstractBuiltRoleWithFactor = AbstractRoleBuilderOrBuilt; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs index 7de5d2257..db79bed7e 100644 --- a/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_builder.rs @@ -220,6 +220,12 @@ impl SecurityShieldBuilder { debug!("Add FactorSource to ConfirmationRole result: {:?}", res); }) } + + pub fn reset_recovery_and_confirmation_role_state(&self) -> &Self { + self.set(|builder| { + builder.reset_recovery_and_confirmation_role_state(); + }) + } } impl SecurityShieldBuilder { From 007758e49091acc9ad1a50ca5067f5b50f7977d9 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 16:00:18 +0100 Subject: [PATCH 62/70] (temp) try fix `MethodTooLargeException` by commenting out method `reset_recovery_and_confirmation_role_state` --- .../security_shield_builder.rs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 67c3492d8..19484583d 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -225,11 +225,9 @@ impl SecurityShieldBuilder { }) } - pub fn reset_recovery_and_confirmation_role_state(&self) { - self.set(|builder| { - builder.reset_recovery_and_confirmation_role_state() - }); - } + // pub fn reset_recovery_and_confirmation_role_state(&self) { + // self.set(|builder| builder.reset_recovery_and_confirmation_role_state()) + // } } #[uniffi::export] @@ -534,15 +532,15 @@ mod tests { FactorSourceID::sample_ledger_other() ] ); - sut.reset_recovery_and_confirmation_role_state(); - assert_eq!(sut.get_recovery_factors(), vec![]); - - sut.add_factor_source_to_recovery_override( - FactorSourceID::sample_ledger(), - ); - sut.add_factor_source_to_recovery_override( - FactorSourceID::sample_ledger_other(), - ); + // sut.reset_recovery_and_confirmation_role_state(); + // assert_eq!(sut.get_recovery_factors(), vec![]); + + // sut.add_factor_source_to_recovery_override( + // FactorSourceID::sample_ledger(), + // ); + // sut.add_factor_source_to_recovery_override( + // FactorSourceID::sample_ledger_other(), + // ); assert_eq!( sut.get_recovery_factors(), From 2f3de0c7df3444960618a9574dac1742ee983982 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 4 Dec 2024 16:36:33 +0100 Subject: [PATCH 63/70] reintroduce reset_recovery_and_confirmation_role_state - will result in scary --- .../security_shield_builder.rs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 19484583d..874e3e100 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -225,9 +225,9 @@ impl SecurityShieldBuilder { }) } - // pub fn reset_recovery_and_confirmation_role_state(&self) { - // self.set(|builder| builder.reset_recovery_and_confirmation_role_state()) - // } + pub fn reset_recovery_and_confirmation_role_state(&self) { + self.set(|builder| builder.reset_recovery_and_confirmation_role_state()) + } } #[uniffi::export] @@ -532,15 +532,15 @@ mod tests { FactorSourceID::sample_ledger_other() ] ); - // sut.reset_recovery_and_confirmation_role_state(); - // assert_eq!(sut.get_recovery_factors(), vec![]); - - // sut.add_factor_source_to_recovery_override( - // FactorSourceID::sample_ledger(), - // ); - // sut.add_factor_source_to_recovery_override( - // FactorSourceID::sample_ledger_other(), - // ); + sut.reset_recovery_and_confirmation_role_state(); + assert_eq!(sut.get_recovery_factors(), vec![]); + + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger(), + ); + sut.add_factor_source_to_recovery_override( + FactorSourceID::sample_ledger_other(), + ); assert_eq!( sut.get_recovery_factors(), From e69d9bdb7b49e10b2eb5432392f058dfc89a7cf5 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Thu, 5 Dec 2024 09:59:26 +0100 Subject: [PATCH 64/70] Fix MethodTooLargeException by lowering method amount by 2. --- .../security_shield_builder.rs | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 874e3e100..48107e614 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -337,6 +337,26 @@ impl SecurityShieldBuilder { ) } + // HERE BE DRAGONS + // Because of `MethodTooLargeException`, see my issue on UniFFIs Github: + // https://github.com/mozilla/uniffi-rs/issues/2340 + // + // We need to temporarily restrict number of methods. So we merge three together into one. + // Once we have fixed the issue we can go back to use the three methods below. + pub fn validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( + &self, + role_kind: RoleKind, + factor_sources: Vec, + ) -> Vec { + match role_kind { + RoleKind::Primary => self.validation_for_addition_of_factor_source_to_primary_override_for_each(factor_sources), + RoleKind::Recovery => self.validation_for_addition_of_factor_source_to_recovery_override_for_each(factor_sources), + RoleKind::Confirmation => self.validation_for_addition_of_factor_source_to_confirmation_override_for_each(factor_sources), + } + } +} + +impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( &self, factor_sources: Vec, @@ -573,28 +593,35 @@ mod tests { assert_ne!( sim_prim, - sut.validation_for_addition_of_factor_source_to_primary_override_for_each(vec![ + sut.validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( + RoleKind::Primary, + vec![ FactorSourceID::sample_arculus(), ]) ); assert_ne!( sim_prim_threshold, - sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each(vec![ + sut.validation_for_addition_of_factor_source_to_primary_threshold_for_each( + vec![ FactorSourceID::sample_arculus() ]) ); assert_ne!( sim_rec, - sut.validation_for_addition_of_factor_source_to_recovery_override_for_each(vec![ - FactorSourceID::sample_ledger(), - ]) - ); + sut.validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( + RoleKind::Recovery, + vec![ + FactorSourceID::sample_ledger(), + ]) + ); assert_ne!( - sim_conf, - sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each(vec![ + sim_conf, + sut.validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( + RoleKind::Confirmation, + vec![ FactorSourceID::sample_device(), ]) ); From 3ee614f141059f48f324cdd4b3440a8f8a1ff194 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Thu, 5 Dec 2024 12:28:52 +0100 Subject: [PATCH 65/70] Simplify macro_rules impl which declared `MatrixOFFactor***` with `preinterpret` crate instead of `paste` crate. --- Cargo.lock | 11 ++ crates/sargon-uniffi/Cargo.toml | 1 + .../matrices/decl_matrix_macro.rs | 162 ++++++++---------- 3 files changed, 80 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aebf634be..929a6edd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2234,6 +2234,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "preinterpret" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4cb4d87d808156f56cf409c26dfc93d2cc9e37a6a407a5ebcaca0cf9850789" +dependencies = [ + "proc-macro2", + "syn 2.0.85", +] + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -2832,6 +2842,7 @@ dependencies = [ "itertools 0.12.0", "log", "paste 1.0.14", + "preinterpret", "pretty_assertions", "pretty_env_logger", "radix-engine-toolkit", diff --git a/crates/sargon-uniffi/Cargo.toml b/crates/sargon-uniffi/Cargo.toml index 7e2fab6c3..78c439666 100644 --- a/crates/sargon-uniffi/Cargo.toml +++ b/crates/sargon-uniffi/Cargo.toml @@ -114,6 +114,7 @@ async-trait = { git = "https://github.com/dtolnay/async-trait", rev = "1eb21ed8b pretty_assertions = { git = "https://github.com/rust-pretty-assertions/rust-pretty-assertions", rev = "3f1ebc0cac5f88e875f036bf16636e15fa935c8d" } base64 = { git = "https://github.com/marshallpierce/rust-base64.git", rev = "e14400697453bcc85997119b874bc03d9601d0af" } +preinterpret = "0.1.3" # Fixes nasty iOS bug "_kSecMatchSubjectWholeString", see https://github.com/kornelski/rust-security-framework/issues/203 # This is a workaround to fix a bug with version 2.11.0 that added some symbols that are not available on iOS diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs index f5753981f..d64430902 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/decl_matrix_macro.rs @@ -1,115 +1,89 @@ use crate::prelude::*; +use preinterpret::*; + macro_rules! matrix_conversion { - // Impl From -> crate - (from_internal: $internal:ident, $uniffi:ident) => { - impl From<$internal> for $uniffi { - fn from(value: $internal) -> Self { - Self { - primary_role: value.primary().clone().into(), - recovery_role: value.recovery().clone().into(), - confirmation_role: value.confirmation().clone().into(), - number_of_days_until_auto_confirm: value - .number_of_days_until_auto_confirm, - } + ( + // $( + // #[doc = $expr: expr] + // )* + $(#[$attributes:meta])* + $factor_level: ident + ) => { + preinterpret::preinterpret! { + [!set! #internal_factor = [!ident! Internal $factor_level]] + [!set! #struct_name = [!ident! MatrixOf $factor_level s]] + [!set! #internal_struct_name = [!ident! Internal #struct_name]] + [!set! #primary_role_type = [!ident! PrimaryRoleWith $factor_level s ]] + [!set! #recovery_role_type = [!ident! RecoveryRoleWith $factor_level s ]] + [!set! #confirmation_role_type = [!ident! ConfirmationRoleWith $factor_level s ]] + + use sargon::#struct_name as #internal_struct_name; + use sargon::$factor_level as #internal_factor; + + $(#[$attributes])* + #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] + pub struct #struct_name { + pub primary_role: #primary_role_type, + pub recovery_role: #recovery_role_type, + pub confirmation_role: #confirmation_role_type, + + pub number_of_days_until_auto_confirm: u16, } - } - }; - // Impl From -> Internal - (to_internal: $internal_factor:ty => $uniffi:ident, $internal:ident) => { - impl $uniffi { - pub fn into_internal(&self) -> $internal { - unsafe { - <$internal>::unbuilt_with_roles_and_days( - self.primary_role.clone().into(), - self.recovery_role.clone().into(), - self.confirmation_role.clone().into(), - self.number_of_days_until_auto_confirm, - ) + delegate_debug_into!(#struct_name, #internal_struct_name); + + impl From<#internal_struct_name> for #struct_name { + fn from(value: #internal_struct_name) -> Self { + Self { + primary_role: value.primary().clone().into(), + recovery_role: value.recovery().clone().into(), + confirmation_role: value.confirmation().clone().into(), + number_of_days_until_auto_confirm: value + .number_of_days_until_auto_confirm, + } } } - } - impl From<$uniffi> for $internal { - fn from(value: $uniffi) -> Self { - value.into_internal() - } - } - }; - // Impl `From for X` and `From for InternalX` - // and impl `HasSampleValues` for X` and UniFFI export `new_X_sample` and `new_X_sample_other`. - (impl_from: $factor_level:ty => $uniffi_name:ident => $internal_name:ident ) => { - matrix_conversion!( - from_internal: $internal_name, $uniffi_name - ); - matrix_conversion!( - to_internal: $factor_level => $uniffi_name, $internal_name - ); - impl HasSampleValues for $uniffi_name { - fn sample() -> Self { - $internal_name::sample().into() + impl #struct_name { + pub fn into_internal(&self) -> #internal_struct_name { + unsafe { + #internal_struct_name::unbuilt_with_roles_and_days( + self.primary_role.clone().into(), + self.recovery_role.clone().into(), + self.confirmation_role.clone().into(), + self.number_of_days_until_auto_confirm, + ) + } + } } - fn sample_other() -> Self { - $internal_name::sample_other().into() + impl From<#struct_name> for #internal_struct_name { + fn from(value: #struct_name) -> Self { + value.into_internal() + } } - } - paste! { - #[uniffi::export] - pub fn [< new_ $uniffi_name:snake _ sample >]() -> $uniffi_name { - $uniffi_name::sample() + impl HasSampleValues for #struct_name { + fn sample() -> Self { + #internal_struct_name::sample().into() + } + fn sample_other() -> Self { + #internal_struct_name::sample_other().into() + } } + [!set! #fn_name_prefix = new_[!snake_case! #struct_name]] + #[uniffi::export] - pub fn [< new_ $uniffi_name:snake _ sample_other >]() -> $uniffi_name { - $uniffi_name::sample_other() + pub fn [!ident! #fn_name_prefix _sample ]() -> #struct_name { + #struct_name::sample() } - } - }; - ( - struct: - $( - #[doc = $expr: expr] - )* - $struct_name:ident, $factor_level:ty - ) => { - paste! { - $( - #[doc = $expr] - )* - #[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)] - pub struct $struct_name { - pub primary_role: [< PrimaryRoleWith $factor_level s >], - pub recovery_role: [< RecoveryRoleWith $factor_level s >], - pub confirmation_role: [< ConfirmationRoleWith $factor_level s >], - pub number_of_days_until_auto_confirm: u16, + #[uniffi::export] + pub fn [!ident! #fn_name_prefix _sample_other ]() -> #struct_name { + #struct_name::sample_other() } - use sargon::$struct_name as [< Internal $struct_name>]; - delegate_debug_into!($struct_name, [< Internal $struct_name>]); - matrix_conversion!( - impl_from: [< Internal $factor_level>] => $struct_name => [< Internal $struct_name >] - ); - } - }; - ( - $( - #[doc = $expr: expr] - )* - $factor_level:ty - ) => { - paste! { - use sargon::$factor_level as [< Internal $factor_level>]; - matrix_conversion!( - struct: - $( - #[doc = $expr] - )* - [< MatrixOf $factor_level s >], - $factor_level - ); } }; } From 9f6611862daedae872b0eb6864e771b203365d05 Mon Sep 17 00:00:00 2001 From: matiasbzurovski <164921079+matiasbzurovski@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:22:31 +0100 Subject: [PATCH 66/70] Check prerequisites for building a Security Shield (#292) * Add FactorSourceCategory & checking of SecurityShieldBuildingPrerequisites --- .../matrices/matrix_of_factor_source_ids.rs | 34 +++--- .../profile/mfa/security_structures/mod.rs | 2 + .../security_shield_builder.rs | 2 +- .../security_shield_prerequisites_status.rs | 19 +++ .../sargon_os_security_structures.rs | 14 +++ .../account/query_security_structures.rs | 103 ++++++++++++++++ .../builder/matrix_builder_unit_tests.rs | 20 ++-- .../matrices/builder/matrix_template.rs | 76 ++++++------ .../matrices/matrix_of_factor_source_ids.rs | 112 +++++++++--------- .../profile/mfa/security_structures/mod.rs | 2 + .../security_shield_prerequisites_status.rs | 16 +++ .../v100/factors/factor_source_category.rs | 17 +++ .../v100/factors/factor_source_kind.rs | 37 ++++++ .../profile/v100/factors/is_factor_source.rs | 4 + crates/sargon/src/profile/v100/factors/mod.rs | 2 + .../sargon_os_security_structures.rs | 22 ++++ 16 files changed, 360 insertions(+), 122 deletions(-) create mode 100644 crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs create mode 100644 crates/sargon/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs create mode 100644 crates/sargon/src/profile/v100/factors/factor_source_category.rs diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index 6fcee16a5..7f6c3f651 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -20,20 +20,20 @@ macro_rules! export_sample_config { pub(crate) use export_sample_config; -export_sample_config!(11); -export_sample_config!(12); -export_sample_config!(13); -export_sample_config!(14); -export_sample_config!(15); -export_sample_config!(21); -export_sample_config!(22); -export_sample_config!(23); -export_sample_config!(24); -export_sample_config!(30); -export_sample_config!(40); -export_sample_config!(51); -export_sample_config!(52); -export_sample_config!(60); -export_sample_config!(70); -export_sample_config!(80); -export_sample_config!(90); +export_sample_config!(1_1); +export_sample_config!(1_2); +export_sample_config!(1_3); +export_sample_config!(1_4); +export_sample_config!(1_5); +export_sample_config!(2_1); +export_sample_config!(2_2); +export_sample_config!(2_3); +export_sample_config!(2_4); +export_sample_config!(3_0); +export_sample_config!(4_0); +export_sample_config!(5_1); +export_sample_config!(5_2); +export_sample_config!(6_0); +export_sample_config!(7_0); +export_sample_config!(8_0); +export_sample_config!(9_0); diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs index d1c312969..8c0137ec5 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/mod.rs @@ -3,6 +3,7 @@ mod models; mod roles; mod security_shield_builder; mod security_shield_builder_invalid_reason; +mod security_shield_prerequisites_status; mod security_structure_id; mod security_structure_metadata; mod security_structures; @@ -11,6 +12,7 @@ pub use matrices::*; pub use models::*; pub use roles::*; pub use security_shield_builder_invalid_reason::*; +pub use security_shield_prerequisites_status::*; pub use security_structure_id::*; pub use security_structure_metadata::*; pub use security_structures::*; diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index 48107e614..c6da0853b 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -179,7 +179,7 @@ impl SecurityShieldBuilder { /// Adds the factor source to the primary role threshold list. /// - /// Also sets the threshold to 1 this is the first factor set and if + /// Also sets the threshold to 1 if this is the first factor set and if /// the threshold was 0. pub fn add_factor_source_to_primary_threshold( &self, diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs new file mode 100644 index 000000000..f3ef1c3b8 --- /dev/null +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs @@ -0,0 +1,19 @@ +use crate::prelude::*; +use sargon::SecurityShieldPrerequisitesStatus as InternalSecurityShieldPrerequisitesStatus; + +/// An enum representing the status of the prerequisites for building a Security Shield. +/// This is, whether the user has the necessary factor sources to build a Security Shield. +#[derive( + Clone, Copy, Debug, PartialEq, Eq, InternalConversion, uniffi::Enum, +)] +pub enum SecurityShieldPrerequisitesStatus { + /// A Security Shield can be built with the current Factor Sources available. + Sufficient, + + /// At least one hardware Factor Source must be added in order to build a Shield. + /// Note: this doesn't mean that after adding a hardware Factor Source we would have `Sufficient` status. + HardwareRequired, + + /// One more Factor Source, of any category, must be added in order to build a Shield. + AnyRequired, +} diff --git a/crates/sargon-uniffi/src/system/sargon_os/sargon_os_security_structures.rs b/crates/sargon-uniffi/src/system/sargon_os/sargon_os_security_structures.rs index 9f04b9d07..156502106 100644 --- a/crates/sargon-uniffi/src/system/sargon_os/sargon_os_security_structures.rs +++ b/crates/sargon-uniffi/src/system/sargon_os/sargon_os_security_structures.rs @@ -56,4 +56,18 @@ impl SargonOS { .await .into_result() } + + /// Returns the status of the prerequisites for building a Security Shield. + /// + /// According to [definition][doc], a Security Shield can be built if the user has, asides from + /// the Identity factor, "2 or more factors, one of which must be Hardware" + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Factor-Prerequisites + pub fn security_shield_prerequisites_status( + &self, + ) -> Result { + self.wrapped + .security_shield_prerequisites_status() + .into_result() + } } diff --git a/crates/sargon/src/profile/logic/account/query_security_structures.rs b/crates/sargon/src/profile/logic/account/query_security_structures.rs index dad7be110..5b9885408 100644 --- a/crates/sargon/src/profile/logic/account/query_security_structures.rs +++ b/crates/sargon/src/profile/logic/account/query_security_structures.rs @@ -37,3 +37,106 @@ impl Profile { .collect::>() } } + +impl Profile { + /// Returns the status of the prerequisites for building a Security Shield. + /// + /// According to [definition][doc], a Security Shield can be built if the user has, asides from + /// the Identity factor, "2 or more factors, one of which must be Hardware" + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Factor-Prerequisites + pub fn security_shield_prerequisites_status( + &self, + ) -> SecurityShieldPrerequisitesStatus { + let factor_sources = self.factor_sources.clone(); + let count_excluding_identity = factor_sources + .iter() + .filter(|f| f.category() != FactorSourceCategory::Identity) + .count(); + let count_hardware = factor_sources + .iter() + .filter(|f| f.category() == FactorSourceCategory::Hardware) + .count(); + if count_hardware < 1 { + SecurityShieldPrerequisitesStatus::HardwareRequired + } else if count_excluding_identity < 2 { + SecurityShieldPrerequisitesStatus::AnyRequired + } else { + SecurityShieldPrerequisitesStatus::Sufficient + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = Profile; + + #[test] + fn security_shield_prerequisites_status_hardware_required() { + let mut sut = SUT::sample(); + + // Test the case where user doesn't have any factors + sut.factor_sources = FactorSources::from_iter([]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::HardwareRequired); + + // Test the case where the user has identity factor + sut.factor_sources = + FactorSources::from_iter([FactorSource::sample_device()]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::HardwareRequired); + + // Test the case where the user also has other non-hardware factors + sut.factor_sources = FactorSources::from_iter([ + FactorSource::sample_device(), + FactorSource::sample_password(), + FactorSource::sample_trusted_contact_frank(), + FactorSource::sample_off_device(), + ]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::HardwareRequired); + } + + #[test] + fn security_shield_prerequisites_status_any_required() { + let mut sut = SUT::sample(); + + // Test the case where user only has hardware factor + sut.factor_sources = + FactorSources::from_iter([FactorSource::sample_arculus()]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::AnyRequired); + + // Test the case where the user also has identity factors + sut.factor_sources = FactorSources::from_iter([ + FactorSource::sample_arculus(), + FactorSource::sample_device(), + ]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::AnyRequired); + } + + #[test] + fn security_shield_prerequisites_status_sufficient() { + let mut sut = SUT::sample(); + + // Test the case where user only has hardware factors + sut.factor_sources = FactorSources::from_iter([ + FactorSource::sample_arculus(), + FactorSource::sample_ledger(), + ]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::Sufficient); + + // Test the case where the user has 1 hardware factor and 1 non-hardware factor + sut.factor_sources = FactorSources::from_iter([ + FactorSource::sample_ledger(), + FactorSource::sample_password(), + ]); + let result = sut.security_shield_prerequisites_status(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::Sufficient); + } +} diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs index e40c629e4..f7a6f6089 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_builder_unit_tests.rs @@ -1409,7 +1409,7 @@ mod shield_configs { ],), ) ); - assert_eq!(built, MatrixOfFactorSourceIds::sample_config_11()); + assert_eq!(built, MatrixOfFactorSourceIds::sample_config_1_1()); } #[test] @@ -1532,7 +1532,7 @@ mod shield_configs { ); pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_13() + MatrixOfFactorSourceIds::sample_config_1_3() ) } @@ -1581,7 +1581,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_14() + MatrixOfFactorSourceIds::sample_config_1_4() ) } @@ -1630,7 +1630,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_15() + MatrixOfFactorSourceIds::sample_config_1_5() ) } @@ -1691,7 +1691,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_21() + MatrixOfFactorSourceIds::sample_config_2_1() ) } @@ -1752,7 +1752,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_22() + MatrixOfFactorSourceIds::sample_config_2_2() ) } @@ -1801,7 +1801,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_23() + MatrixOfFactorSourceIds::sample_config_2_3() ) } @@ -1850,7 +1850,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_24() + MatrixOfFactorSourceIds::sample_config_2_4() ) } @@ -1916,7 +1916,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_30() + MatrixOfFactorSourceIds::sample_config_3_0() ) } @@ -1987,7 +1987,7 @@ mod shield_configs { pretty_assertions::assert_eq!( built, - MatrixOfFactorSourceIds::sample_config_40() + MatrixOfFactorSourceIds::sample_config_4_0() ) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs index c804b55cf..8fe804f91 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/builder/matrix_template.rs @@ -292,7 +292,7 @@ impl MatrixTemplate { /// Config 5.1 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn config_51() -> Self { + pub fn config_5_1() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -308,7 +308,7 @@ impl MatrixTemplate { /// Config 5.2 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn config_52() -> Self { + pub fn config_5_2() -> Self { Self::new( PrimaryRoleTemplate::new([ FactorSourceTemplate::device(), @@ -413,138 +413,138 @@ mod test_templates { } #[test] - fn template_config_11() { + fn template_config_1_1() { test_template( MatrixTemplate::config_1_1(), - MatrixOfFactorSourceIds::sample_config_11(), + MatrixOfFactorSourceIds::sample_config_1_1(), ) } #[test] - fn template_config_12() { + fn template_config_1_2() { test_template( MatrixTemplate::config_1_2(), - MatrixOfFactorSourceIds::sample_config_12(), + MatrixOfFactorSourceIds::sample_config_1_2(), ) } #[test] - fn template_config_13() { + fn template_config_1_3() { test_template( MatrixTemplate::config_1_3(), - MatrixOfFactorSourceIds::sample_config_13(), + MatrixOfFactorSourceIds::sample_config_1_3(), ) } #[test] - fn template_config_14() { + fn template_config_1_4() { test_template( MatrixTemplate::config_1_4(), - MatrixOfFactorSourceIds::sample_config_14(), + MatrixOfFactorSourceIds::sample_config_1_4(), ) } #[test] - fn template_config_15() { + fn template_config_1_5() { test_template( MatrixTemplate::config_1_5(), - MatrixOfFactorSourceIds::sample_config_15(), + MatrixOfFactorSourceIds::sample_config_1_5(), ) } #[test] - fn template_config_21() { + fn template_config_2_1() { test_template( MatrixTemplate::config_2_1(), - MatrixOfFactorSourceIds::sample_config_21(), + MatrixOfFactorSourceIds::sample_config_2_1(), ) } #[test] - fn template_config_22() { + fn template_config_2_2() { test_template( MatrixTemplate::config_2_2(), - MatrixOfFactorSourceIds::sample_config_22(), + MatrixOfFactorSourceIds::sample_config_2_2(), ) } #[test] - fn template_config_23() { + fn template_config_2_3() { test_template( MatrixTemplate::config_2_3(), - MatrixOfFactorSourceIds::sample_config_23(), + MatrixOfFactorSourceIds::sample_config_2_3(), ) } #[test] - fn template_config_24() { + fn template_config_2_4() { test_template( MatrixTemplate::config_2_4(), - MatrixOfFactorSourceIds::sample_config_24(), + MatrixOfFactorSourceIds::sample_config_2_4(), ) } #[test] - fn template_config_30() { + fn template_config_3_0() { test_template( MatrixTemplate::config_3_0(), - MatrixOfFactorSourceIds::sample_config_30(), + MatrixOfFactorSourceIds::sample_config_3_0(), ) } #[test] - fn template_config_40() { + fn template_config_4_0() { test_template( MatrixTemplate::config_4_0(), - MatrixOfFactorSourceIds::sample_config_40(), + MatrixOfFactorSourceIds::sample_config_4_0(), ) } #[test] - fn template_config_51() { + fn template_config_5_1() { test_template( - MatrixTemplate::config_51(), - MatrixOfFactorSourceIds::sample_config_51(), + MatrixTemplate::config_5_1(), + MatrixOfFactorSourceIds::sample_config_5_1(), ) } #[test] - fn template_config_52() { + fn template_config_5_2() { test_template( - MatrixTemplate::config_52(), - MatrixOfFactorSourceIds::sample_config_52(), + MatrixTemplate::config_5_2(), + MatrixOfFactorSourceIds::sample_config_5_2(), ) } #[test] - fn template_config_60() { + fn template_config_6_0() { test_template( MatrixTemplate::config_6_0(), - MatrixOfFactorSourceIds::sample_config_60(), + MatrixOfFactorSourceIds::sample_config_6_0(), ) } #[test] - fn template_config_70() { + fn template_config_7_0() { test_template( MatrixTemplate::config_7_0(), - MatrixOfFactorSourceIds::sample_config_70(), + MatrixOfFactorSourceIds::sample_config_7_0(), ) } #[test] - fn template_config_80() { + fn template_config_8_0() { test_template( MatrixTemplate::config_8_0(), - MatrixOfFactorSourceIds::sample_config_80(), + MatrixOfFactorSourceIds::sample_config_8_0(), ) } #[test] - fn template_config_90() { + fn template_config_9_0() { test_template( MatrixTemplate::config_9_0(), - MatrixOfFactorSourceIds::sample_config_90(), + MatrixOfFactorSourceIds::sample_config_9_0(), ) } } diff --git a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs index ccec1325c..1ab5c51bb 100644 --- a/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs +++ b/crates/sargon/src/profile/mfa/security_structures/matrices/matrix_of_factor_source_ids.rs @@ -61,130 +61,130 @@ impl MatrixOfFactorSourceIds { /// Config 1.1 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_11() -> Self { + pub fn sample_config_1_1() -> Self { Self::sample_from_template(MatrixTemplate::config_1_1()) } /// Config 1.2 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_12() -> Self { + pub fn sample_config_1_2() -> Self { Self::sample_from_template(MatrixTemplate::config_1_2()) } /// Config 1.3 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_13() -> Self { + pub fn sample_config_1_3() -> Self { Self::sample_from_template(MatrixTemplate::config_1_3()) } /// Config 1.4 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_14() -> Self { + pub fn sample_config_1_4() -> Self { Self::sample_from_template(MatrixTemplate::config_1_4()) } /// Config 1.5 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_15() -> Self { + pub fn sample_config_1_5() -> Self { Self::sample_from_template(MatrixTemplate::config_1_5()) } /// Config 2.1 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_21() -> Self { + pub fn sample_config_2_1() -> Self { Self::sample_from_template(MatrixTemplate::config_2_1()) } /// Config 2.2 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_22() -> Self { + pub fn sample_config_2_2() -> Self { Self::sample_from_template(MatrixTemplate::config_2_2()) } /// Config 2.3 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_23() -> Self { + pub fn sample_config_2_3() -> Self { Self::sample_from_template(MatrixTemplate::config_2_3()) } /// Config 2.4 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_24() -> Self { + pub fn sample_config_2_4() -> Self { Self::sample_from_template(MatrixTemplate::config_2_4()) } /// Config 3.0 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_30() -> Self { + pub fn sample_config_3_0() -> Self { Self::sample_from_template(MatrixTemplate::config_3_0()) } /// Config 4.0 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_40() -> Self { + pub fn sample_config_4_0() -> Self { Self::sample_from_template(MatrixTemplate::config_4_0()) } /// Config 5.1 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_51() -> Self { - Self::sample_from_template(MatrixTemplate::config_51()) + pub fn sample_config_5_1() -> Self { + Self::sample_from_template(MatrixTemplate::config_5_1()) } /// Config 5.2 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_52() -> Self { - Self::sample_from_template(MatrixTemplate::config_52()) + pub fn sample_config_5_2() -> Self { + Self::sample_from_template(MatrixTemplate::config_5_2()) } /// Config 6.0 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_60() -> Self { + pub fn sample_config_6_0() -> Self { Self::sample_from_template(MatrixTemplate::config_6_0()) } /// Config 7.0 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_70() -> Self { + pub fn sample_config_7_0() -> Self { Self::sample_from_template(MatrixTemplate::config_7_0()) } /// Config 8.0 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_80() -> Self { + pub fn sample_config_8_0() -> Self { Self::sample_from_template(MatrixTemplate::config_8_0()) } /// Config 9.0 according to [this document][doc]. /// /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Example-Security-Shield-Configurations - pub fn sample_config_90() -> Self { + pub fn sample_config_9_0() -> Self { Self::sample_from_template(MatrixTemplate::config_9_0()) } } impl HasSampleValues for MatrixOfFactorSourceIds { fn sample() -> Self { - Self::sample_config_11() + Self::sample_config_1_1() } fn sample_other() -> Self { - Self::sample_config_24() + Self::sample_config_2_4() } } @@ -206,7 +206,7 @@ mod tests { #[test] fn inequality() { assert_ne!(SUT::sample(), SUT::sample_other()); - assert_ne!(SUT::sample(), SUT::sample_config_12()); + assert_ne!(SUT::sample(), SUT::sample_config_1_2()); assert_ne!(SUT::sample().primary(), SUT::sample_other().primary()); assert_ne!(SUT::sample().recovery(), SUT::sample_other().recovery()); assert_ne!( @@ -219,41 +219,41 @@ mod tests { fn hash() { assert_eq!( HashSet::::from_iter([ - SUT::sample_config_11(), - SUT::sample_config_12(), - SUT::sample_config_13(), - SUT::sample_config_14(), - SUT::sample_config_15(), - SUT::sample_config_21(), - SUT::sample_config_22(), - SUT::sample_config_23(), - SUT::sample_config_24(), - SUT::sample_config_30(), - SUT::sample_config_40(), - SUT::sample_config_51(), - SUT::sample_config_52(), - SUT::sample_config_60(), - SUT::sample_config_70(), - SUT::sample_config_80(), - SUT::sample_config_90(), + SUT::sample_config_1_1(), + SUT::sample_config_1_2(), + SUT::sample_config_1_3(), + SUT::sample_config_1_4(), + SUT::sample_config_1_5(), + SUT::sample_config_2_1(), + SUT::sample_config_2_2(), + SUT::sample_config_2_3(), + SUT::sample_config_2_4(), + SUT::sample_config_3_0(), + SUT::sample_config_4_0(), + SUT::sample_config_5_1(), + SUT::sample_config_5_2(), + SUT::sample_config_6_0(), + SUT::sample_config_7_0(), + SUT::sample_config_8_0(), + SUT::sample_config_9_0(), // Duplicates should be removed - SUT::sample_config_11(), - SUT::sample_config_12(), - SUT::sample_config_13(), - SUT::sample_config_14(), - SUT::sample_config_15(), - SUT::sample_config_21(), - SUT::sample_config_22(), - SUT::sample_config_23(), - SUT::sample_config_24(), - SUT::sample_config_30(), - SUT::sample_config_40(), - SUT::sample_config_51(), - SUT::sample_config_52(), - SUT::sample_config_60(), - SUT::sample_config_70(), - SUT::sample_config_80(), - SUT::sample_config_90(), + SUT::sample_config_1_1(), + SUT::sample_config_1_2(), + SUT::sample_config_1_3(), + SUT::sample_config_1_4(), + SUT::sample_config_1_5(), + SUT::sample_config_2_1(), + SUT::sample_config_2_2(), + SUT::sample_config_2_3(), + SUT::sample_config_2_4(), + SUT::sample_config_3_0(), + SUT::sample_config_4_0(), + SUT::sample_config_5_1(), + SUT::sample_config_5_2(), + SUT::sample_config_6_0(), + SUT::sample_config_7_0(), + SUT::sample_config_8_0(), + SUT::sample_config_9_0(), ]) .len(), 17 diff --git a/crates/sargon/src/profile/mfa/security_structures/mod.rs b/crates/sargon/src/profile/mfa/security_structures/mod.rs index abf0f0d13..3c90ae55f 100644 --- a/crates/sargon/src/profile/mfa/security_structures/mod.rs +++ b/crates/sargon/src/profile/mfa/security_structures/mod.rs @@ -3,6 +3,7 @@ mod matrices; mod roles; mod security_shield_builder; mod security_shield_builder_invalid_reason; +mod security_shield_prerequisites_status; mod security_structure_id; mod security_structure_metadata; mod security_structure_of_factors; @@ -12,6 +13,7 @@ pub use matrices::*; pub use roles::*; pub use security_shield_builder::*; pub use security_shield_builder_invalid_reason::*; +pub use security_shield_prerequisites_status::*; pub use security_structure_id::*; pub use security_structure_metadata::*; pub use security_structure_of_factors::*; diff --git a/crates/sargon/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs b/crates/sargon/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs new file mode 100644 index 000000000..087827ae6 --- /dev/null +++ b/crates/sargon/src/profile/mfa/security_structures/security_shield_prerequisites_status.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; + +/// An enum representing the status of the prerequisites for building a Security Shield. +/// This is, whether the user has the necessary factor sources to build a Security Shield. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SecurityShieldPrerequisitesStatus { + /// A Security Shield can be built with the current Factor Sources available. + Sufficient, + + /// At least one hardware Factor Source must be added in order to build a Shield. + /// Note: this doesn't mean that after adding a hardware Factor Source we would have `Sufficient` status. + HardwareRequired, + + /// One more Factor Source, of any category, must be added in order to build a Shield. + AnyRequired, +} diff --git a/crates/sargon/src/profile/v100/factors/factor_source_category.rs b/crates/sargon/src/profile/v100/factors/factor_source_category.rs new file mode 100644 index 000000000..3d9d4f900 --- /dev/null +++ b/crates/sargon/src/profile/v100/factors/factor_source_category.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +/// An enum representing the **category** of a `FactorSource`/`FactorSourceKind`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FactorSourceCategory { + /// Something I am. + Identity, + + /// Something I have. + Hardware, + + /// Something I know. + Information, + + /// Someone I trust. + Contact, +} diff --git a/crates/sargon/src/profile/v100/factors/factor_source_kind.rs b/crates/sargon/src/profile/v100/factors/factor_source_kind.rs index e4f5a5b91..5d60b958f 100644 --- a/crates/sargon/src/profile/v100/factors/factor_source_kind.rs +++ b/crates/sargon/src/profile/v100/factors/factor_source_kind.rs @@ -104,6 +104,20 @@ impl FactorSourceKind { } } +impl FactorSourceKind { + pub fn category(&self) -> FactorSourceCategory { + use FactorSourceCategory::*; + match self { + Self::LedgerHQHardwareWallet | Self::ArculusCard => Hardware, + Self::Password + | Self::SecurityQuestions + | Self::OffDeviceMnemonic => Information, + Self::Device => Identity, + Self::TrustedContact => Contact, + } + } +} + impl HasSampleValues for FactorSourceKind { fn sample() -> Self { Self::Device @@ -205,6 +219,29 @@ mod tests { assert_eq!(SUT::Password.discriminant(), "password"); } + #[test] + fn category() { + assert_eq!( + SUT::LedgerHQHardwareWallet.category(), + FactorSourceCategory::Hardware + ); + assert_eq!(SUT::ArculusCard.category(), FactorSourceCategory::Hardware); + assert_eq!(SUT::Password.category(), FactorSourceCategory::Information); + assert_eq!( + SUT::SecurityQuestions.category(), + FactorSourceCategory::Information + ); + assert_eq!( + SUT::OffDeviceMnemonic.category(), + FactorSourceCategory::Information + ); + assert_eq!(SUT::Device.category(), FactorSourceCategory::Identity); + assert_eq!( + SUT::TrustedContact.category(), + FactorSourceCategory::Contact + ); + } + #[test] fn display() { assert_eq!(format!("{}", SUT::Device.discriminant()), "device"); diff --git a/crates/sargon/src/profile/v100/factors/is_factor_source.rs b/crates/sargon/src/profile/v100/factors/is_factor_source.rs index 79f6218ce..b25e1d940 100644 --- a/crates/sargon/src/profile/v100/factors/is_factor_source.rs +++ b/crates/sargon/src/profile/v100/factors/is_factor_source.rs @@ -19,6 +19,10 @@ pub trait BaseBaseIsFactorSource { } fn name(&self) -> String; + + fn category(&self) -> FactorSourceCategory { + self.factor_source_kind().category() + } } pub trait BaseIsFactorSource: diff --git a/crates/sargon/src/profile/v100/factors/mod.rs b/crates/sargon/src/profile/v100/factors/mod.rs index f75663847..f70c985bd 100644 --- a/crates/sargon/src/profile/v100/factors/mod.rs +++ b/crates/sargon/src/profile/v100/factors/mod.rs @@ -1,5 +1,6 @@ mod factor_instance; mod factor_source; +mod factor_source_category; mod factor_source_common; mod factor_source_crypto_parameters; mod factor_source_flag; @@ -18,6 +19,7 @@ mod is_factor_source; pub use factor_instance::*; pub use factor_source::*; +pub use factor_source_category::*; pub use factor_source_common::*; pub use factor_source_crypto_parameters::*; pub use factor_source_flag::*; diff --git a/crates/sargon/src/system/sargon_os/sargon_os_security_structures.rs b/crates/sargon/src/system/sargon_os/sargon_os_security_structures.rs index dadd187f3..e01bb00f4 100644 --- a/crates/sargon/src/system/sargon_os/sargon_os_security_structures.rs +++ b/crates/sargon/src/system/sargon_os/sargon_os_security_structures.rs @@ -100,6 +100,21 @@ impl SargonOS { } } +impl SargonOS { + /// Returns the status of the prerequisites for building a Security Shield. + /// + /// According to [definition][doc], a Security Shield can be built if the user has, asides from + /// the Identity factor, "2 or more factors, one of which must be Hardware" + /// + /// [doc]: https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields#Factor-Prerequisites + pub fn security_shield_prerequisites_status( + &self, + ) -> Result { + self.profile_state_holder + .access_profile_with(|p| p.security_shield_prerequisites_status()) + } +} + #[cfg(test)] mod tests { @@ -316,4 +331,11 @@ mod tests { assert_eq!(sources_by_id_lookup, structures); } + + #[actix_rt::test] + async fn security_shield_prerequisites_status() { + let os = SUT::fast_boot().await; + let result = os.security_shield_prerequisites_status().unwrap(); + assert_eq!(result, SecurityShieldPrerequisitesStatus::HardwareRequired); + } } From aa4b2ce09c27e5c74ea475eb612ce63d37f22e48 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 6 Dec 2024 11:42:37 +0100 Subject: [PATCH 67/70] reintroduce [too many for JNA] methods --- .../security_shield_builder.rs | 29 ++----------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs index c6da0853b..4d334469c 100644 --- a/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs +++ b/crates/sargon-uniffi/src/profile/mfa/security_structures/security_shield_builder.rs @@ -337,26 +337,6 @@ impl SecurityShieldBuilder { ) } - // HERE BE DRAGONS - // Because of `MethodTooLargeException`, see my issue on UniFFIs Github: - // https://github.com/mozilla/uniffi-rs/issues/2340 - // - // We need to temporarily restrict number of methods. So we merge three together into one. - // Once we have fixed the issue we can go back to use the three methods below. - pub fn validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( - &self, - role_kind: RoleKind, - factor_sources: Vec, - ) -> Vec { - match role_kind { - RoleKind::Primary => self.validation_for_addition_of_factor_source_to_primary_override_for_each(factor_sources), - RoleKind::Recovery => self.validation_for_addition_of_factor_source_to_recovery_override_for_each(factor_sources), - RoleKind::Confirmation => self.validation_for_addition_of_factor_source_to_confirmation_override_for_each(factor_sources), - } - } -} - -impl SecurityShieldBuilder { pub fn validation_for_addition_of_factor_source_to_primary_override_for_each( &self, factor_sources: Vec, @@ -593,8 +573,7 @@ mod tests { assert_ne!( sim_prim, - sut.validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( - RoleKind::Primary, + sut.validation_for_addition_of_factor_source_to_primary_override_for_each( vec![ FactorSourceID::sample_arculus(), ]) @@ -610,8 +589,7 @@ mod tests { assert_ne!( sim_rec, - sut.validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( - RoleKind::Recovery, + sut.validation_for_addition_of_factor_source_to_recovery_override_for_each( vec![ FactorSourceID::sample_ledger(), ]) @@ -619,8 +597,7 @@ mod tests { assert_ne!( sim_conf, - sut.validation_for_addition_of_factor_source_to_override_for_role_for_each_factor( - RoleKind::Confirmation, + sut.validation_for_addition_of_factor_source_to_confirmation_override_for_each( vec![ FactorSourceID::sample_device(), ]) From fcdaccbeadfac746558161b95f5b52d3731f35f7 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 6 Dec 2024 11:54:08 +0100 Subject: [PATCH 68/70] Fix JDK bug with my fork of UniFFI - will do a PR to UniFFI later. --- Cargo.lock | 43 +++++++++++++++++++++++---------- crates/sargon-uniffi/Cargo.toml | 12 ++++----- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e67a8bac4..40b0d6c5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2208,7 +2208,17 @@ version = "1.4.0" source = "git+https://github.com/rust-pretty-assertions/rust-pretty-assertions?rev=3f1ebc0cac5f88e875f036bf16636e15fa935c8d#3f1ebc0cac5f88e875f036bf16636e15fa935c8d" dependencies = [ "diff", - "yansi", + "yansi 0.5.1", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi 1.0.1", ] [[package]] @@ -2798,7 +2808,7 @@ dependencies = [ "log", "once_cell 1.19.0", "paste 1.0.14", - "pretty_assertions", + "pretty_assertions 1.4.0", "pretty_env_logger", "radix-common", "radix-common-derive", @@ -2846,7 +2856,7 @@ dependencies = [ "log", "paste 1.0.14", "preinterpret", - "pretty_assertions", + "pretty_assertions 1.4.0", "pretty_env_logger", "radix-engine-toolkit", "rand", @@ -3699,7 +3709,7 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "camino 1.1.9", @@ -3714,7 +3724,7 @@ dependencies = [ [[package]] name = "uniffi_bindgen" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "camino 1.1.9", @@ -3725,6 +3735,7 @@ dependencies = [ "heck 0.5.0", "once_cell 1.20.2", "paste 1.0.15", + "pretty_assertions 1.4.1", "rinja", "serde", "textwrap", @@ -3737,7 +3748,7 @@ dependencies = [ [[package]] name = "uniffi_build" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "camino 1.1.9", @@ -3747,7 +3758,7 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "quote", "syn 2.0.85", @@ -3756,7 +3767,7 @@ dependencies = [ [[package]] name = "uniffi_core" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "bytes", @@ -3768,7 +3779,7 @@ dependencies = [ [[package]] name = "uniffi_macros" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "bincode", "camino 1.1.9", @@ -3785,7 +3796,7 @@ dependencies = [ [[package]] name = "uniffi_meta" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "bytes", @@ -3796,7 +3807,7 @@ dependencies = [ [[package]] name = "uniffi_testing" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "camino 1.1.9", @@ -3808,7 +3819,7 @@ dependencies = [ [[package]] name = "uniffi_udl" version = "0.28.3" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "anyhow", "textwrap", @@ -4074,7 +4085,7 @@ dependencies = [ [[package]] name = "weedle2" version = "5.0.0" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=2c003b16d1e70e1914b5d8ceb517eef3676cd187#2c003b16d1e70e1914b5d8ceb517eef3676cd187" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" dependencies = [ "nom", ] @@ -4281,6 +4292,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/crates/sargon-uniffi/Cargo.toml b/crates/sargon-uniffi/Cargo.toml index 86b4985c0..f6603efd9 100644 --- a/crates/sargon-uniffi/Cargo.toml +++ b/crates/sargon-uniffi/Cargo.toml @@ -80,8 +80,8 @@ itertools = { git = "https://github.com/rust-itertools/itertools/", rev = "98eca # enum-as-inner = "0.6.0" enum-as-inner = { git = "https://github.com/bluejekyll/enum-as-inner/", rev = "c15f6e5c4f98ec865e181ae1fff9fc13a1a2e4e2" } -# uniffi = "0.28.3" + unreleased changes -uniffi = { git = "https://github.com/mozilla/uniffi-rs/", rev = "2c003b16d1e70e1914b5d8ceb517eef3676cd187", features = [ +# uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "f937a5ffcd6d25ca96d60be6b987e4fb731d1bca", features = [ "cli", ] } @@ -127,8 +127,8 @@ security-framework-sys = "=2.10.0" [dev-dependencies] -# uniffi = "0.28.3" + unreleased changes -uniffi = { git = "https://github.com/mozilla/uniffi-rs/", rev = "2c003b16d1e70e1914b5d8ceb517eef3676cd187", features = [ +# uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "f937a5ffcd6d25ca96d60be6b987e4fb731d1bca", features = [ "bindgen-tests", ] } @@ -136,8 +136,8 @@ uniffi = { git = "https://github.com/mozilla/uniffi-rs/", rev = "2c003b16d1e70e1 actix-rt = { git = "https://github.com/actix/actix-net", rev = "57fd6ea8098d1f2d281c305fc331216c4fe1992e" } [build-dependencies] -# uniffi = "0.28.3" + unreleased changes -uniffi = { git = "https://github.com/mozilla/uniffi-rs/", rev = "2c003b16d1e70e1914b5d8ceb517eef3676cd187", features = [ +# uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "f937a5ffcd6d25ca96d60be6b987e4fb731d1bca", features = [ "build", ] } From 2f7ae45669cb4d0185ee06fa6a73b0ace1743166 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 6 Dec 2024 12:24:14 +0100 Subject: [PATCH 69/70] bump UniFFI (Sajjon fork) --- Cargo.lock | 43 ++++++++++----------------------- crates/sargon-uniffi/Cargo.toml | 6 ++--- 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40b0d6c5c..091d3653a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2208,17 +2208,7 @@ version = "1.4.0" source = "git+https://github.com/rust-pretty-assertions/rust-pretty-assertions?rev=3f1ebc0cac5f88e875f036bf16636e15fa935c8d#3f1ebc0cac5f88e875f036bf16636e15fa935c8d" dependencies = [ "diff", - "yansi 0.5.1", -] - -[[package]] -name = "pretty_assertions" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" -dependencies = [ - "diff", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -2808,7 +2798,7 @@ dependencies = [ "log", "once_cell 1.19.0", "paste 1.0.14", - "pretty_assertions 1.4.0", + "pretty_assertions", "pretty_env_logger", "radix-common", "radix-common-derive", @@ -2856,7 +2846,7 @@ dependencies = [ "log", "paste 1.0.14", "preinterpret", - "pretty_assertions 1.4.0", + "pretty_assertions", "pretty_env_logger", "radix-engine-toolkit", "rand", @@ -3709,7 +3699,7 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "camino 1.1.9", @@ -3724,7 +3714,7 @@ dependencies = [ [[package]] name = "uniffi_bindgen" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "camino 1.1.9", @@ -3735,7 +3725,6 @@ dependencies = [ "heck 0.5.0", "once_cell 1.20.2", "paste 1.0.15", - "pretty_assertions 1.4.1", "rinja", "serde", "textwrap", @@ -3748,7 +3737,7 @@ dependencies = [ [[package]] name = "uniffi_build" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "camino 1.1.9", @@ -3758,7 +3747,7 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "quote", "syn 2.0.85", @@ -3767,7 +3756,7 @@ dependencies = [ [[package]] name = "uniffi_core" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "bytes", @@ -3779,7 +3768,7 @@ dependencies = [ [[package]] name = "uniffi_macros" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "bincode", "camino 1.1.9", @@ -3796,7 +3785,7 @@ dependencies = [ [[package]] name = "uniffi_meta" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "bytes", @@ -3807,7 +3796,7 @@ dependencies = [ [[package]] name = "uniffi_testing" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "camino 1.1.9", @@ -3819,7 +3808,7 @@ dependencies = [ [[package]] name = "uniffi_udl" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "anyhow", "textwrap", @@ -4085,7 +4074,7 @@ dependencies = [ [[package]] name = "weedle2" version = "5.0.0" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=f937a5ffcd6d25ca96d60be6b987e4fb731d1bca#f937a5ffcd6d25ca96d60be6b987e4fb731d1bca" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" dependencies = [ "nom", ] @@ -4292,12 +4281,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/crates/sargon-uniffi/Cargo.toml b/crates/sargon-uniffi/Cargo.toml index f6603efd9..8fa215690 100644 --- a/crates/sargon-uniffi/Cargo.toml +++ b/crates/sargon-uniffi/Cargo.toml @@ -81,7 +81,7 @@ itertools = { git = "https://github.com/rust-itertools/itertools/", rev = "98eca enum-as-inner = { git = "https://github.com/bluejekyll/enum-as-inner/", rev = "c15f6e5c4f98ec865e181ae1fff9fc13a1a2e4e2" } # uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix -uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "f937a5ffcd6d25ca96d60be6b987e4fb731d1bca", features = [ +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "cd73c548429e786e4fe7b818514da1bc1ee6ff94", features = [ "cli", ] } @@ -128,7 +128,7 @@ security-framework-sys = "=2.10.0" [dev-dependencies] # uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix -uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "f937a5ffcd6d25ca96d60be6b987e4fb731d1bca", features = [ +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "cd73c548429e786e4fe7b818514da1bc1ee6ff94", features = [ "bindgen-tests", ] } @@ -137,7 +137,7 @@ actix-rt = { git = "https://github.com/actix/actix-net", rev = "57fd6ea8098d1f2d [build-dependencies] # uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix -uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "f937a5ffcd6d25ca96d60be6b987e4fb731d1bca", features = [ +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "cd73c548429e786e4fe7b818514da1bc1ee6ff94", features = [ "build", ] } From e2476b15698d4cc8105e06131e32563868c7f508 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 6 Dec 2024 13:28:14 +0100 Subject: [PATCH 70/70] bump uniffi (sajjon fork) --- Cargo.lock | 20 ++++++++++---------- crates/sargon-uniffi/Cargo.toml | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 091d3653a..c5d80a175 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3699,7 +3699,7 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uniffi" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "camino 1.1.9", @@ -3714,7 +3714,7 @@ dependencies = [ [[package]] name = "uniffi_bindgen" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "camino 1.1.9", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "uniffi_build" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "camino 1.1.9", @@ -3747,7 +3747,7 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "quote", "syn 2.0.85", @@ -3756,7 +3756,7 @@ dependencies = [ [[package]] name = "uniffi_core" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "bytes", @@ -3768,7 +3768,7 @@ dependencies = [ [[package]] name = "uniffi_macros" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "bincode", "camino 1.1.9", @@ -3785,7 +3785,7 @@ dependencies = [ [[package]] name = "uniffi_meta" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "bytes", @@ -3796,7 +3796,7 @@ dependencies = [ [[package]] name = "uniffi_testing" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "camino 1.1.9", @@ -3808,7 +3808,7 @@ dependencies = [ [[package]] name = "uniffi_udl" version = "0.28.3" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "anyhow", "textwrap", @@ -4074,7 +4074,7 @@ dependencies = [ [[package]] name = "weedle2" version = "5.0.0" -source = "git+https://github.com/sajjon/uniffi-rs/?rev=cd73c548429e786e4fe7b818514da1bc1ee6ff94#cd73c548429e786e4fe7b818514da1bc1ee6ff94" +source = "git+https://github.com/sajjon/uniffi-rs/?rev=fe40d992b32103876112713cb5e8303b54647183#fe40d992b32103876112713cb5e8303b54647183" dependencies = [ "nom", ] diff --git a/crates/sargon-uniffi/Cargo.toml b/crates/sargon-uniffi/Cargo.toml index 8fa215690..d164a90e1 100644 --- a/crates/sargon-uniffi/Cargo.toml +++ b/crates/sargon-uniffi/Cargo.toml @@ -81,7 +81,7 @@ itertools = { git = "https://github.com/rust-itertools/itertools/", rev = "98eca enum-as-inner = { git = "https://github.com/bluejekyll/enum-as-inner/", rev = "c15f6e5c4f98ec865e181ae1fff9fc13a1a2e4e2" } # uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix -uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "cd73c548429e786e4fe7b818514da1bc1ee6ff94", features = [ +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "fe40d992b32103876112713cb5e8303b54647183", features = [ "cli", ] } @@ -128,7 +128,7 @@ security-framework-sys = "=2.10.0" [dev-dependencies] # uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix -uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "cd73c548429e786e4fe7b818514da1bc1ee6ff94", features = [ +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "fe40d992b32103876112713cb5e8303b54647183", features = [ "bindgen-tests", ] } @@ -137,7 +137,7 @@ actix-rt = { git = "https://github.com/actix/actix-net", rev = "57fd6ea8098d1f2d [build-dependencies] # uniffi = PRE "0.29.X" + Alex Cyons unmerged JNA Kotlin binding fix -uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "cd73c548429e786e4fe7b818514da1bc1ee6ff94", features = [ +uniffi = { git = "https://github.com/sajjon/uniffi-rs/", rev = "fe40d992b32103876112713cb5e8303b54647183", features = [ "build", ] }