Skip to content

Commit fbfb3e5

Browse files
Add type-safety for passport and drivers license dates
1 parent cfb438b commit fbfb3e5

7 files changed

Lines changed: 183 additions & 72 deletions

File tree

crates/bitwarden-core/uniffi.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,27 @@ ffi_module_name = "BitwardenCoreFFI"
1616
module_name = "BitwardenCore"
1717
generate_immutable_records = true
1818
generate_codable_conformance = true
19+
20+
[bindings.swift.custom_types.NaiveDate]
21+
type_name = "Date"
22+
imports = ["Foundation"]
23+
into_custom = """
24+
{
25+
let formatter = DateFormatter()
26+
formatter.locale = Locale(identifier: "en_US_POSIX")
27+
formatter.timeZone = TimeZone(identifier: "UTC")
28+
formatter.dateFormat = "yyyy-MM-dd"
29+
formatter.isLenient = false
30+
return formatter.date(from: {})
31+
}()
32+
"""
33+
from_custom = """
34+
{
35+
let formatter = DateFormatter()
36+
formatter.locale = Locale(identifier: "en_US_POSIX")
37+
formatter.timeZone = TimeZone(identifier: "UTC")
38+
formatter.dateFormat = "yyyy-MM-dd"
39+
formatter.isLenient = false
40+
return formatter.string(from: {})
41+
}()
42+
"""

crates/bitwarden-vault/src/cipher/blob/conversions/drivers_license.rs

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,40 @@
11
use super::{DriversLicenseDataV1, DriversLicenseView};
22

3-
impl_bidirectional_from!(
4-
DriversLicenseView,
5-
DriversLicenseDataV1,
6-
[
7-
first_name,
8-
middle_name,
9-
last_name,
10-
date_of_birth,
11-
license_number,
12-
issuing_country,
13-
issuing_state,
14-
issue_date,
15-
expiration_date,
16-
issuing_authority,
17-
license_class,
18-
]
19-
);
3+
impl From<&DriversLicenseView> for DriversLicenseDataV1 {
4+
fn from(src: &DriversLicenseView) -> Self {
5+
Self {
6+
first_name: src.first_name.clone(),
7+
middle_name: src.middle_name.clone(),
8+
last_name: src.last_name.clone(),
9+
date_of_birth: src.date_of_birth.map(|d| d.to_string()),
10+
license_number: src.license_number.clone(),
11+
issuing_country: src.issuing_country.clone(),
12+
issuing_state: src.issuing_state.clone(),
13+
issue_date: src.issue_date.map(|d| d.to_string()),
14+
expiration_date: src.expiration_date.map(|d| d.to_string()),
15+
issuing_authority: src.issuing_authority.clone(),
16+
license_class: src.license_class.clone(),
17+
}
18+
}
19+
}
20+
21+
impl From<&DriversLicenseDataV1> for DriversLicenseView {
22+
fn from(src: &DriversLicenseDataV1) -> Self {
23+
Self {
24+
first_name: src.first_name.clone(),
25+
middle_name: src.middle_name.clone(),
26+
last_name: src.last_name.clone(),
27+
date_of_birth: src.date_of_birth.as_deref().and_then(|s| s.parse().ok()),
28+
license_number: src.license_number.clone(),
29+
issuing_country: src.issuing_country.clone(),
30+
issuing_state: src.issuing_state.clone(),
31+
issue_date: src.issue_date.as_deref().and_then(|s| s.parse().ok()),
32+
expiration_date: src.expiration_date.as_deref().and_then(|s| s.parse().ok()),
33+
issuing_authority: src.issuing_authority.clone(),
34+
license_class: src.license_class.clone(),
35+
}
36+
}
37+
}
2038

2139
#[cfg(test)]
2240
mod tests {
@@ -36,12 +54,12 @@ mod tests {
3654
first_name: Some("John".to_string()),
3755
middle_name: Some("Michael".to_string()),
3856
last_name: Some("Doe".to_string()),
39-
date_of_birth: Some("1985-06-15".to_string()),
57+
date_of_birth: chrono::NaiveDate::from_ymd_opt(1985, 6, 15),
4058
license_number: Some("DL-987654".to_string()),
4159
issuing_country: Some("US".to_string()),
4260
issuing_state: Some("NY".to_string()),
43-
issue_date: Some("2020-01-01".to_string()),
44-
expiration_date: Some("2028-01-01".to_string()),
61+
issue_date: chrono::NaiveDate::from_ymd_opt(2020, 1, 1),
62+
expiration_date: chrono::NaiveDate::from_ymd_opt(2028, 1, 1),
4563
issuing_authority: Some("NY DMV".to_string()),
4664
license_class: Some("D".to_string()),
4765
}),

crates/bitwarden-vault/src/cipher/blob/conversions/passport.rs

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
use super::{PassportDataV1, PassportView};
22

3-
impl_bidirectional_from!(
4-
PassportView,
5-
PassportDataV1,
6-
[
7-
surname,
8-
given_name,
9-
date_of_birth,
10-
sex,
11-
birth_place,
12-
nationality,
13-
issuing_country,
14-
passport_number,
15-
passport_type,
16-
national_identification_number,
17-
issuing_authority,
18-
issue_date,
19-
expiration_date,
20-
]
21-
);
3+
impl From<&PassportView> for PassportDataV1 {
4+
fn from(src: &PassportView) -> Self {
5+
Self {
6+
surname: src.surname.clone(),
7+
given_name: src.given_name.clone(),
8+
date_of_birth: src.date_of_birth.map(|d| d.to_string()),
9+
sex: src.sex.clone(),
10+
birth_place: src.birth_place.clone(),
11+
nationality: src.nationality.clone(),
12+
issuing_country: src.issuing_country.clone(),
13+
passport_number: src.passport_number.clone(),
14+
passport_type: src.passport_type.clone(),
15+
national_identification_number: src.national_identification_number.clone(),
16+
issuing_authority: src.issuing_authority.clone(),
17+
issue_date: src.issue_date.map(|d| d.to_string()),
18+
expiration_date: src.expiration_date.map(|d| d.to_string()),
19+
}
20+
}
21+
}
22+
23+
impl From<&PassportDataV1> for PassportView {
24+
fn from(src: &PassportDataV1) -> Self {
25+
Self {
26+
surname: src.surname.clone(),
27+
given_name: src.given_name.clone(),
28+
date_of_birth: src.date_of_birth.as_deref().and_then(|s| s.parse().ok()),
29+
sex: src.sex.clone(),
30+
birth_place: src.birth_place.clone(),
31+
nationality: src.nationality.clone(),
32+
issuing_country: src.issuing_country.clone(),
33+
passport_number: src.passport_number.clone(),
34+
passport_type: src.passport_type.clone(),
35+
national_identification_number: src.national_identification_number.clone(),
36+
issuing_authority: src.issuing_authority.clone(),
37+
issue_date: src.issue_date.as_deref().and_then(|s| s.parse().ok()),
38+
expiration_date: src.expiration_date.as_deref().and_then(|s| s.parse().ok()),
39+
}
40+
}
41+
}
2242

2343
#[cfg(test)]
2444
mod tests {
@@ -37,7 +57,7 @@ mod tests {
3757
passport: Some(PassportView {
3858
surname: Some("Doe".to_string()),
3959
given_name: Some("Jane".to_string()),
40-
date_of_birth: Some("1990-01-01".to_string()),
60+
date_of_birth: chrono::NaiveDate::from_ymd_opt(1990, 1, 1),
4161
sex: Some("F".to_string()),
4262
birth_place: Some("New York".to_string()),
4363
nationality: Some("American".to_string()),
@@ -46,8 +66,8 @@ mod tests {
4666
passport_type: Some("P".to_string()),
4767
national_identification_number: Some("123-45-6789".to_string()),
4868
issuing_authority: Some("US State Department".to_string()),
49-
issue_date: Some("2020-01-01".to_string()),
50-
expiration_date: Some("2030-01-01".to_string()),
69+
issue_date: chrono::NaiveDate::from_ymd_opt(2020, 1, 1),
70+
expiration_date: chrono::NaiveDate::from_ymd_opt(2030, 1, 1),
5171
}),
5272
..create_shell_cipher_view(CipherType::Passport)
5373
};

crates/bitwarden-vault/src/cipher/cipher.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3488,7 +3488,7 @@ mod tests {
34883488
let passport = passport::PassportView {
34893489
given_name: Some("Jane".to_string()),
34903490
surname: Some("Doe".to_string()),
3491-
date_of_birth: Some("1990-01-01".to_string()),
3491+
date_of_birth: chrono::NaiveDate::from_ymd_opt(1990, 1, 1),
34923492
sex: Some("F".to_string()),
34933493
birth_place: Some("New York".to_string()),
34943494
nationality: Some("American".to_string()),
@@ -3497,8 +3497,8 @@ mod tests {
34973497
passport_type: Some("P".to_string()),
34983498
national_identification_number: Some("123-45-6789".to_string()),
34993499
issuing_authority: Some("US State Department".to_string()),
3500-
issue_date: Some("2020-01-01".to_string()),
3501-
expiration_date: Some("2030-01-01".to_string()),
3500+
issue_date: chrono::NaiveDate::from_ymd_opt(2020, 1, 1),
3501+
expiration_date: chrono::NaiveDate::from_ymd_opt(2030, 1, 1),
35023502
};
35033503

35043504
let cipher_view = CipherView {
@@ -3526,12 +3526,12 @@ mod tests {
35263526
first_name: Some("John".to_string()),
35273527
middle_name: Some("Michael".to_string()),
35283528
last_name: Some("Doe".to_string()),
3529-
date_of_birth: Some("1985-06-15".to_string()),
3529+
date_of_birth: chrono::NaiveDate::from_ymd_opt(1985, 6, 15),
35303530
license_number: Some("DL-987654".to_string()),
35313531
issuing_country: Some("US".to_string()),
35323532
issuing_state: Some("NY".to_string()),
3533-
issue_date: Some("2020-01-01".to_string()),
3534-
expiration_date: Some("2028-01-01".to_string()),
3533+
issue_date: chrono::NaiveDate::from_ymd_opt(2020, 1, 1),
3534+
expiration_date: chrono::NaiveDate::from_ymd_opt(2028, 1, 1),
35353535
issuing_authority: Some("NY DMV".to_string()),
35363536
license_class: Some("D".to_string()),
35373537
};

crates/bitwarden-vault/src/cipher/drivers_license.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use bitwarden_crypto::{
44
CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext,
55
PrimitiveEncryptable,
66
};
7+
use chrono::NaiveDate;
78
use serde::{Deserialize, Serialize};
89
#[cfg(feature = "wasm")]
910
use tsify::Tsify;
@@ -38,12 +39,12 @@ pub struct DriversLicenseView {
3839
pub first_name: Option<String>,
3940
pub middle_name: Option<String>,
4041
pub last_name: Option<String>,
41-
pub date_of_birth: Option<String>,
42+
pub date_of_birth: Option<NaiveDate>,
4243
pub license_number: Option<String>,
4344
pub issuing_country: Option<String>,
4445
pub issuing_state: Option<String>,
45-
pub issue_date: Option<String>,
46-
pub expiration_date: Option<String>,
46+
pub issue_date: Option<NaiveDate>,
47+
pub expiration_date: Option<NaiveDate>,
4748
pub issuing_authority: Option<String>,
4849
pub license_class: Option<String>,
4950
}
@@ -58,12 +59,18 @@ impl CompositeEncryptable<KeySlotIds, SymmetricKeySlotId, DriversLicense> for Dr
5859
first_name: self.first_name.encrypt(ctx, key)?,
5960
middle_name: self.middle_name.encrypt(ctx, key)?,
6061
last_name: self.last_name.encrypt(ctx, key)?,
61-
date_of_birth: self.date_of_birth.encrypt(ctx, key)?,
62+
date_of_birth: self
63+
.date_of_birth
64+
.map(|d| d.to_string())
65+
.encrypt(ctx, key)?,
6266
license_number: self.license_number.encrypt(ctx, key)?,
6367
issuing_country: self.issuing_country.encrypt(ctx, key)?,
6468
issuing_state: self.issuing_state.encrypt(ctx, key)?,
65-
issue_date: self.issue_date.encrypt(ctx, key)?,
66-
expiration_date: self.expiration_date.encrypt(ctx, key)?,
69+
issue_date: self.issue_date.map(|d| d.to_string()).encrypt(ctx, key)?,
70+
expiration_date: self
71+
.expiration_date
72+
.map(|d| d.to_string())
73+
.encrypt(ctx, key)?,
6774
issuing_authority: self.issuing_authority.encrypt(ctx, key)?,
6875
license_class: self.license_class.encrypt(ctx, key)?,
6976
})
@@ -80,12 +87,27 @@ impl Decryptable<KeySlotIds, SymmetricKeySlotId, DriversLicenseView> for Drivers
8087
first_name: self.first_name.decrypt(ctx, key).ok().flatten(),
8188
middle_name: self.middle_name.decrypt(ctx, key).ok().flatten(),
8289
last_name: self.last_name.decrypt(ctx, key).ok().flatten(),
83-
date_of_birth: self.date_of_birth.decrypt(ctx, key).ok().flatten(),
90+
date_of_birth: self
91+
.date_of_birth
92+
.decrypt(ctx, key)
93+
.ok()
94+
.flatten()
95+
.and_then(|s: String| s.parse().ok()),
8496
license_number: self.license_number.decrypt(ctx, key).ok().flatten(),
8597
issuing_country: self.issuing_country.decrypt(ctx, key).ok().flatten(),
8698
issuing_state: self.issuing_state.decrypt(ctx, key).ok().flatten(),
87-
issue_date: self.issue_date.decrypt(ctx, key).ok().flatten(),
88-
expiration_date: self.expiration_date.decrypt(ctx, key).ok().flatten(),
99+
issue_date: self
100+
.issue_date
101+
.decrypt(ctx, key)
102+
.ok()
103+
.flatten()
104+
.and_then(|s: String| s.parse().ok()),
105+
expiration_date: self
106+
.expiration_date
107+
.decrypt(ctx, key)
108+
.ok()
109+
.flatten()
110+
.and_then(|s: String| s.parse().ok()),
89111
issuing_authority: self.issuing_authority.decrypt(ctx, key).ok().flatten(),
90112
license_class: self.license_class.decrypt(ctx, key).ok().flatten(),
91113
})
@@ -200,12 +222,12 @@ mod tests {
200222
first_name: Some("John".to_string()),
201223
middle_name: Some("Michael".to_string()),
202224
last_name: Some("Doe".to_string()),
203-
date_of_birth: Some("1985-06-15".to_string()),
225+
date_of_birth: NaiveDate::from_ymd_opt(1985, 6, 15),
204226
license_number: Some("DL-987654".to_string()),
205227
issuing_country: Some("US".to_string()),
206228
issuing_state: Some("NY".to_string()),
207-
issue_date: Some("2020-01-01".to_string()),
208-
expiration_date: Some("2028-01-01".to_string()),
229+
issue_date: NaiveDate::from_ymd_opt(2020, 1, 1),
230+
expiration_date: NaiveDate::from_ymd_opt(2028, 1, 1),
209231
issuing_authority: Some("NY DMV".to_string()),
210232
license_class: Some("D".to_string()),
211233
}

0 commit comments

Comments
 (0)