diff --git a/crates/sargon-uniffi/src/profile/v100/entity_security_state/provisional_securified_config.rs b/crates/sargon-uniffi/src/profile/v100/entity_security_state/provisional_securified_config.rs index ce62514b9..ebf499462 100644 --- a/crates/sargon-uniffi/src/profile/v100/entity_security_state/provisional_securified_config.rs +++ b/crates/sargon-uniffi/src/profile/v100/entity_security_state/provisional_securified_config.rs @@ -4,12 +4,28 @@ use sargon::{ ProvisionalSecurifiedTransactionQueued as InternalProvisionalSecurifiedTransactionQueued, }; +/// A tuple of a `SecurityStructureOfFactorInstances` and a `TransactionIntentHash` +/// which represents a queued transaction to which is changing the security structure +/// if some entity. Since this provisional state is set on the entity itself, no +/// need to store the entity address here. #[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Record)] pub struct ProvisionalSecurifiedTransactionQueued { + /// The FactorInstances we are changing to. pub factor_instances: SecurityStructureOfFactorInstances, + + /// The ID of the queued transaction which is changing the security structure + /// to `factor_instances`. pub txid: TransactionIntentHash, } +/// The different intermediary states of changing the security structure of an entity. +/// This type is put in an `Option` on either `UnsecuredEntityControl` or `SecurifiedEntityControl`, +/// and if `None` it means user has no provisionally changed security structure. If set, it contains +/// these different variants: +/// * `ShieldSelected` - User has selected which security shield to use for some entity, +/// * `FactorInstancesDerived` - Sargon has provided a `SecurityStructureOfFactorInstances` but +/// user has not made a transaction to apply it to the entity yet. +/// * `TransactionQueued` - User has signed and queued a transaction changing to `SecurityStructureOfFactorInstances` #[derive(Clone, PartialEq, Eq, Hash, InternalConversion, uniffi::Enum)] pub enum ProvisionalSecurifiedConfig { /// User has selected which security shield to use for some entity, 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 4ce6af7d9..05434c5d7 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 @@ -47,13 +47,112 @@ impl HasProvisionalSecurifiedConfig for EntitySecurityState { } } +impl<'de> Deserialize<'de> for EntitySecurityState { + #[cfg(not(tarpaulin_include))] // false negative + fn deserialize>( + deserializer: D, + ) -> Result { + // https://github.com/serde-rs/serde/issues/1343#issuecomment-409698470 + #[derive(Deserialize, Serialize)] + struct Wrapper { + #[serde(flatten, with = "EntitySecurityState")] + value: EntitySecurityState, + } + Wrapper::deserialize(deserializer).map(|w| w.value) + } +} + +impl Serialize for EntitySecurityState { + #[cfg(not(tarpaulin_include))] // false negative + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = + serializer.serialize_struct("EntitySecurityState", 2)?; + match self { + EntitySecurityState::Unsecured { value } => { + state.serialize_field("discriminator", "unsecured")?; + state.serialize_field("unsecuredEntityControl", value)?; + } + EntitySecurityState::Securified { value } => { + state.serialize_field("discriminator", "securified")?; + state.serialize_field("securedEntityControl", value)?; + } + } + state.end() + } +} + +impl From for EntitySecurityState { + fn from(value: UnsecuredEntityControl) -> Self { + Self::Unsecured { value } + } +} + +impl From for EntitySecurityState { + fn from(value: SecuredEntityControl) -> Self { + Self::Securified { value } + } +} + +impl HasSampleValues for EntitySecurityState { + /// A sample used to facilitate unit tests. + fn sample() -> Self { + Self::Unsecured { + value: UnsecuredEntityControl::sample(), + } + } + + /// A sample used to facilitate unit tests. + fn sample_other() -> Self { + Self::Unsecured { + value: UnsecuredEntityControl::sample_other(), + } + } +} + +impl HasFactorInstances for EntitySecurityState { + fn unique_tx_signing_factor_instances(&self) -> IndexSet { + match self { + EntitySecurityState::Unsecured { value } => { + value.unique_tx_signing_factor_instances() + } + EntitySecurityState::Securified { value } => { + value.unique_tx_signing_factor_instances() + } + } + } + fn unique_all_factor_instances(&self) -> IndexSet { + match self { + EntitySecurityState::Unsecured { value } => { + value.unique_all_factor_instances() + } + EntitySecurityState::Securified { value } => { + value.unique_all_factor_instances() + } + } + } +} + #[cfg(test)] -mod has_provisional_securified_config_tests { +mod tests { use super::*; #[allow(clippy::upper_case_acronyms)] type SUT = EntitySecurityState; + #[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 set_provisional_is_err_when_provisional_is_tx_queued(sut: SUT) { let mut sut = sut; sut.set_provisional(ProvisionalSecurifiedConfig::TransactionQueued { @@ -217,113 +316,6 @@ mod has_provisional_securified_config_tests { SecuredEntityControl::sample_other(), )) } -} - -impl<'de> Deserialize<'de> for EntitySecurityState { - #[cfg(not(tarpaulin_include))] // false negative - fn deserialize>( - deserializer: D, - ) -> Result { - // https://github.com/serde-rs/serde/issues/1343#issuecomment-409698470 - #[derive(Deserialize, Serialize)] - struct Wrapper { - #[serde(flatten, with = "EntitySecurityState")] - value: EntitySecurityState, - } - Wrapper::deserialize(deserializer).map(|w| w.value) - } -} - -impl Serialize for EntitySecurityState { - #[cfg(not(tarpaulin_include))] // false negative - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = - serializer.serialize_struct("EntitySecurityState", 2)?; - match self { - EntitySecurityState::Unsecured { value } => { - state.serialize_field("discriminator", "unsecured")?; - state.serialize_field("unsecuredEntityControl", value)?; - } - EntitySecurityState::Securified { value } => { - state.serialize_field("discriminator", "securified")?; - state.serialize_field("securedEntityControl", value)?; - } - } - state.end() - } -} - -impl From for EntitySecurityState { - fn from(value: UnsecuredEntityControl) -> Self { - Self::Unsecured { value } - } -} - -impl From for EntitySecurityState { - fn from(value: SecuredEntityControl) -> Self { - Self::Securified { value } - } -} - -impl HasSampleValues for EntitySecurityState { - /// A sample used to facilitate unit tests. - fn sample() -> Self { - Self::Unsecured { - value: UnsecuredEntityControl::sample(), - } - } - - /// A sample used to facilitate unit tests. - fn sample_other() -> Self { - Self::Unsecured { - value: UnsecuredEntityControl::sample_other(), - } - } -} - -impl HasFactorInstances for EntitySecurityState { - fn unique_tx_signing_factor_instances(&self) -> IndexSet { - match self { - EntitySecurityState::Unsecured { value } => { - value.unique_tx_signing_factor_instances() - } - EntitySecurityState::Securified { value } => { - value.unique_tx_signing_factor_instances() - } - } - } - fn unique_all_factor_instances(&self) -> IndexSet { - match self { - EntitySecurityState::Unsecured { value } => { - value.unique_all_factor_instances() - } - EntitySecurityState::Securified { value } => { - value.unique_all_factor_instances() - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = EntitySecurityState; - - #[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 json_roundtrip_unsecurified() { diff --git a/crates/sargon/src/profile/v100/entity_security_state/provisional_securified_transaction_queued.rs b/crates/sargon/src/profile/v100/entity_security_state/provisional_securified_transaction_queued.rs index 18f296f89..14c8851d2 100644 --- a/crates/sargon/src/profile/v100/entity_security_state/provisional_securified_transaction_queued.rs +++ b/crates/sargon/src/profile/v100/entity_security_state/provisional_securified_transaction_queued.rs @@ -8,6 +8,7 @@ use crate::prelude::*; pub struct ProvisionalSecurifiedTransactionQueued { /// The FactorInstances we are changing to. pub factor_instances: SecurityStructureOfFactorInstances, + /// The ID of the queued transaction which is changing the security structure /// to `factor_instances`. pub txid: TransactionIntentHash,