Skip to content

Commit

Permalink
Security Shield builder API updates (#343)
Browse files Browse the repository at this point in the history
* Uniffi expose primary threshold

* Add remove all override factors function

* Update shield validation

* Add time period as recovery  emergency fallback

* Add samples. Add test

* Add samples. Add test

* Add extensions

* Add swift tests

* Expose functions and add tests

* PR review

* Version bump
  • Loading branch information
sergiupuhalschi-rdx authored Jan 17, 2025
1 parent 50f84ed commit 28ac8b8
Show file tree
Hide file tree
Showing 104 changed files with 845 additions and 238 deletions.
166 changes: 83 additions & 83 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation
import SargonUniFFI

extension TimePeriod {
public init(days: Int) {
self = newTimePeriodWithDays(value: UInt16(days))
}

public func days() -> Int {
Int(timePeriodToDays(timePeriod: self))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation
import SargonUniFFI

extension TimePeriodUnit {
public var values: [Int] {
Array(1 ... Int(constantMaxRecoveryConfirmationFallbackPeriodUnits()))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation
import SargonUniFFI

#if DEBUG
extension Threshold {
public static let sample: Self = newThresholdSample()
public static let sampleOther: Self = newThresholdSampleOther()
}
#endif // DEBUG
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation
import SargonUniFFI

#if DEBUG
extension TimePeriod {
public static let sample: Self = newTimePeriodSample()
public static let sampleOther: Self = newTimePeriodSampleOther()
}
#endif // DEBUG
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ extension SecurityShieldBuilder {
public typealias Factor = FactorSourceID

/// Confirmation Role
public var numberOfDaysUntilAutoConfirm: UInt16 {
getNumberOfDaysUntilAutoConfirm()
public var timePeriodUntilAutoConfirm: TimePeriod {
getTimePeriodUntilAutoConfirm()
}

public var threshold: UInt8 {
public var threshold: Threshold {
getPrimaryThreshold()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Foundation
import SargonUniFFI

extension Threshold: SargonModel {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Foundation
import SargonUniFFI

extension TimePeriod: SargonModel {}
4 changes: 4 additions & 0 deletions apple/Sources/Sargon/Util/SharedConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ extension Account {
extension Persona {
public static let nameMaxLength = Int(constantEntityNameMaxLength())
}

extension DisplayName {
public static let maxLength = Int(constantDisplayNameMaxLength())
}
54 changes: 25 additions & 29 deletions apple/Tests/TestCases/Profile/MFA/SecurityShieldsBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ struct ShieldTests {
@Test("threshold")
func threshold() {
var builder = SecurityShieldBuilder()
#expect(builder.threshold == 0)
#expect(builder.threshold == Threshold.all)
builder = builder.setThreshold(threshold: Threshold.specific(42))
#expect(builder.threshold == 42)
#expect(builder.threshold == Threshold.specific(42))
}

@Test("days")
func days() {
var builder = SecurityShieldBuilder()
#expect(builder.numberOfDaysUntilAutoConfirm == 14)
builder = builder.setNumberOfDaysUntilAutoConfirm(numberOfDays: 237)
#expect(builder.numberOfDaysUntilAutoConfirm == 237)
#expect(builder.timePeriodUntilAutoConfirm == TimePeriod(days: 14))
builder = builder.setTimePeriodUntilAutoConfirm(timePeriod: TimePeriod(days: 237))
#expect(builder.timePeriodUntilAutoConfirm == TimePeriod(days: 237))
}

@Test("empty primary threshold")
Expand Down Expand Up @@ -74,23 +74,25 @@ struct ShieldTests {
.addFactorSourceToRecoveryOverride(factorSourceId: y)
#expect(builder.recoveryRoleFactors == [y])

#expect(builder.threshold == 3)
#expect(builder.threshold == Threshold.specific(3))

builder = builder.removeFactorFromPrimary(factorSourceId: x, factorListKind: FactorListKind.threshold)
#expect(builder.threshold == 2)
#expect(builder.threshold == Threshold.specific(2))

builder = builder.removeFactorFromAllRoles(factorSourceId: y)
#expect(builder.recoveryRoleFactors == []) // assert `y` is removed from Recovery and Primary
#expect(builder.threshold == 1)
#expect(builder.threshold == Threshold.specific(1))

builder = builder.removeFactorFromPrimary(factorSourceId: z, factorListKind: FactorListKind.threshold)
#expect(builder.threshold == 0)
#expect(builder.threshold == Threshold.all)
#expect(builder.primaryRoleThresholdFactors == [])
}

@Test("basic validation")
func basicValidation() throws {
var builder = SecurityShieldBuilder()
#expect(builder.validate() == .MissingAuthSigningFactor)
builder = builder.setAuthenticationSigningFactor(new: .sampleDevice)
#expect(builder.validate() == .PrimaryRoleMustHaveAtLeastOneFactor)
builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice)
.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) // did not get added, duplicates are not allowed
Expand All @@ -112,17 +114,9 @@ struct ShieldTests {
#expect((try? builder.build()) != nil)
}

@Test("primary role with threshold factors cannot have a threshold value of zero")
func primaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero() throws {
var builder = SecurityShieldBuilder()
.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleLedger)
.setThreshold(threshold: Threshold.specific(0))
#expect(builder.validate() == .PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero)
}

@Test("cannot add forbidden FactorSourceKinds")
func preventAddOfForbiddenFactorSourceKinds() throws {
var builder = SecurityShieldBuilder()
let builder = SecurityShieldBuilder()
// Primary
.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleTrustedContact) // Verboten
.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleSecurityQuestions) // Verboten
Expand Down Expand Up @@ -161,14 +155,14 @@ struct ShieldTests {
@Test("Primary password never alone")
func primaryPasswordNeverAlone() {
var builder = SecurityShieldBuilder()
.setAuthenticationSigningFactor(new: .sampleDevice)
.addFactorSourceToPrimaryOverride(factorSourceId: .samplePassword) // not allowed
#expect(builder.primaryRoleOverrideFactors.isEmpty)

builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .samplePassword)
#expect(builder.validate() == .PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor)
builder = builder.setThreshold(threshold: Threshold.specific(0))
builder = builder.setThreshold(threshold: Threshold.all)

#expect(builder.validate() == .PrimaryRoleWithThresholdFactorsCannotHaveAThresholdValueOfZero)
builder = builder.setThreshold(threshold: Threshold.specific(1))
#expect(builder.validate() == .PrimaryRoleWithPasswordInThresholdListMustHaveAnotherFactor)
builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleLedger)
Expand All @@ -178,7 +172,7 @@ struct ShieldTests {
builder = builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleArculus)
.addFactorSourceToConfirmationOverride(factorSourceId: .sampleArculusOther)

builder.setAuthenticationSigningFactor(new: .sampleDevice)
builder = builder.setAuthenticationSigningFactor(new: .sampleDevice)

let shield = try! builder.build()

Expand All @@ -191,14 +185,16 @@ struct ShieldTests {
func build() throws {
var builder = SecurityShieldBuilder()
.setName(name: "S.H.I.E.L.D.")
.setNumberOfDaysUntilAutoConfirm(numberOfDays: 42)
.setTimePeriodUntilAutoConfirm(timePeriod: TimePeriod(days: 42))

#expect(builder.validate() == .MissingAuthSigningFactor)
builder = builder.setAuthenticationSigningFactor(new: .sampleDevice)
#expect(builder.validate() == .PrimaryRoleMustHaveAtLeastOneFactor)

// Primary
#expect(builder.threshold == 0)
builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice) // bumps threshold
#expect(builder.threshold == 1)
#expect(builder.threshold == Threshold.all)
builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice)
#expect(builder.threshold == Threshold.all)
builder = builder.addFactorSourceToPrimaryOverride(factorSourceId: .sampleArculus)
.addFactorSourceToPrimaryOverride(factorSourceId: .sampleArculusOther)
// Recovery
Expand All @@ -210,7 +206,7 @@ struct ShieldTests {
.removeFactorFromPrimary(factorSourceId: .sampleArculusOther, factorListKind: FactorListKind.override)
.removeFactorFromRecovery(factorSourceId: .sampleLedgerOther)

builder.setAuthenticationSigningFactor(new: .sampleDevice)
builder = builder.setAuthenticationSigningFactor(new: .sampleDevice)

// Validate
#expect(builder.validate() == nil)
Expand All @@ -234,7 +230,7 @@ struct ShieldTests {

@Test("selected primary threshold factors status")
func selectedPrimaryThresholdFactorsStatusInvalid() {
var builder = SecurityShieldBuilder()
let builder = SecurityShieldBuilder()
.addFactorSourceToPrimaryThreshold(factorSourceId: .samplePassword)

#expect(builder.selectedPrimaryThresholdFactorsStatus() == .invalid(reason: SelectedPrimaryThresholdFactorsStatusInvalidReason.cannotBeUsedAlone(factorSourceKind: FactorSourceKind.password)))
Expand All @@ -246,11 +242,11 @@ struct ShieldTests {

#expect(builder.selectedPrimaryThresholdFactorsStatus() == .insufficient)

builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice)
builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleDevice)

#expect(builder.selectedPrimaryThresholdFactorsStatus() == .suboptimal)

builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleLedger)
builder = builder.addFactorSourceToPrimaryThreshold(factorSourceId: .sampleLedger)

#expect(builder.selectedPrimaryThresholdFactorsStatus() == .optimal)
}
Expand Down
7 changes: 7 additions & 0 deletions apple/Tests/TestCases/Profile/MFA/ThresholdTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import CustomDump
import Foundation
import Sargon
import SargonUniFFI
import XCTest

final class ThresholdTests: Test<Threshold> {}
12 changes: 12 additions & 0 deletions apple/Tests/TestCases/Profile/MFA/TimePeriodTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import CustomDump
import Foundation
import Sargon
import SargonUniFFI
import XCTest

final class TimePeriodTests: Test<TimePeriod> {
func test() {
let sut = SUT(days: 1)
XCTAssertEqual(sut.days(), 1)
}
}
12 changes: 12 additions & 0 deletions apple/Tests/TestCases/Profile/MFA/TimePeriodUnitTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import CustomDump
import Foundation
import Sargon
import SargonUniFFI
import XCTest

final class TimePeriodUnitTests: TestCase {
func test() {
let sut = TimePeriodUnit.days
XCTAssertEqual(sut.values, Array(1 ... 9999))
}
}
2 changes: 1 addition & 1 deletion crates/app/home-cards/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "home-cards"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/app/key-derivation-traits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "key-derivation-traits"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/app/radix-connect-models/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "radix-connect-models"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/app/radix-connect/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "radix-connect"
version = "1.1.108"
version = "1.1.109"
edition = "2021"


Expand Down
2 changes: 1 addition & 1 deletion crates/app/security-center/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "security-center"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/app/signing-traits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "signing-traits"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/app/signing/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "signing"
version = "1.1.108"
version = "1.1.109"
edition = "2021"


Expand Down
2 changes: 1 addition & 1 deletion crates/common/build-info/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "build-info"
version = "1.1.108"
version = "1.1.109"
edition = "2021"
build = "build.rs"

Expand Down
2 changes: 1 addition & 1 deletion crates/common/bytes/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bytes"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/entity-foundation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "entity-foundation"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/host-info/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "host-info"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/identified-vec-of/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "identified-vec-of"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/metadata/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "metadata"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/network/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "network"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/numeric/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "numeric"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/short-string/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "short-string"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion crates/core/assert-json/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "assert-json"
version = "1.1.108"
version = "1.1.109"
edition = "2021"

[dependencies]
Expand Down
Loading

0 comments on commit 28ac8b8

Please sign in to comment.