Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check prerequisites for building a Security Shield #292

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<SecurityShieldPrerequisitesStatus> {
self.wrapped
.security_shield_prerequisites_status()
.into_result()
}
}
103 changes: 103 additions & 0 deletions crates/sargon/src/profile/logic/account/query_security_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,106 @@ impl Profile {
.collect::<Result<SecurityStructuresOfFactorSources>>()
}
}

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ mod shield_configs {
],),
)
);
assert_eq!(built, MatrixOfFactorSourceIds::sample_config_11());
assert_eq!(built, MatrixOfFactorSourceIds::sample_config_1_1());
}

#[test]
Expand Down Expand Up @@ -1532,7 +1532,7 @@ mod shield_configs {
);
pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_13()
MatrixOfFactorSourceIds::sample_config_1_3()
)
}

Expand Down Expand Up @@ -1581,7 +1581,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_14()
MatrixOfFactorSourceIds::sample_config_1_4()
)
}

Expand Down Expand Up @@ -1630,7 +1630,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_15()
MatrixOfFactorSourceIds::sample_config_1_5()
)
}

Expand Down Expand Up @@ -1691,7 +1691,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_21()
MatrixOfFactorSourceIds::sample_config_2_1()
)
}

Expand Down Expand Up @@ -1752,7 +1752,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_22()
MatrixOfFactorSourceIds::sample_config_2_2()
)
}

Expand Down Expand Up @@ -1801,7 +1801,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_23()
MatrixOfFactorSourceIds::sample_config_2_3()
)
}

Expand Down Expand Up @@ -1850,7 +1850,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_24()
MatrixOfFactorSourceIds::sample_config_2_4()
)
}

Expand Down Expand Up @@ -1916,7 +1916,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_30()
MatrixOfFactorSourceIds::sample_config_3_0()
)
}

Expand Down Expand Up @@ -1987,7 +1987,7 @@ mod shield_configs {

pretty_assertions::assert_eq!(
built,
MatrixOfFactorSourceIds::sample_config_40()
MatrixOfFactorSourceIds::sample_config_4_0()
)
}
}
Expand Down
Loading
Loading