Skip to content

Commit b8e494b

Browse files
authored
PM-23639: Add CXF Import mappings (#361)
Adds multiple CXF mappings
1 parent 3355fed commit b8e494b

File tree

16 files changed

+2443
-73
lines changed

16 files changed

+2443
-73
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bitwarden-exporters/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ bitwarden-core = { workspace = true }
3131
bitwarden-crypto = { workspace = true }
3232
bitwarden-error = { workspace = true }
3333
bitwarden-fido = { workspace = true }
34+
bitwarden-ssh = { workspace = true }
3435
bitwarden-vault = { workspace = true }
3536
chrono = { workspace = true, features = ["std"] }
36-
credential-exchange-format = ">=0.1, <0.2"
37+
credential-exchange-format = { git = "https://github.com/bitwarden/credential-exchange", rev = "38e8a013c13644f832c457555baaa536fe481b77" }
3738
csv = "1.3.0"
3839
num-traits = ">=0.2, <0.3"
3940
serde = { workspace = true }

crates/bitwarden-exporters/resources/cxf_example.json

Lines changed: 599 additions & 0 deletions
Large diffs are not rendered by default.

crates/bitwarden-exporters/src/cxf/api_key.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use credential_exchange_format::ApiKeyCredential;
33
use crate::{cxf::editable_field::create_field, Field};
44

55
/// Convert API key credentials to custom fields
6-
pub fn api_key_to_fields(api_key: &ApiKeyCredential) -> Vec<Field> {
6+
pub(super) fn api_key_to_fields(api_key: &ApiKeyCredential) -> Vec<Field> {
77
[
88
api_key.key.as_ref().map(|key| create_field("API Key", key)),
99
api_key

crates/bitwarden-exporters/src/cxf/card.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use chrono::Month;
77
use credential_exchange_format::{Credential, CreditCardCredential, EditableFieldYearMonth};
88
use num_traits::FromPrimitive;
99

10-
use crate::Card;
10+
use crate::{cxf::editable_field::create_field, Card, Field};
1111

1212
impl From<Card> for Vec<Credential> {
1313
fn from(value: Card) -> Self {
@@ -57,6 +57,23 @@ impl From<&CreditCardCredential> for Card {
5757
}
5858
}
5959

60+
pub(super) fn to_card(credential: &CreditCardCredential) -> (Card, Vec<Field>) {
61+
let card = credential.into();
62+
63+
let fields = [
64+
credential.pin.as_ref().map(|v| create_field("PIN", v)),
65+
credential
66+
.valid_from
67+
.as_ref()
68+
.map(|v| create_field("Valid From", v)),
69+
]
70+
.into_iter()
71+
.flatten()
72+
.collect();
73+
74+
(card, fields)
75+
}
76+
6077
/// Sanitize credit card brand
6178
///
6279
/// Performs a fuzzy match on the string to find a matching brand. By converting to lowercase and
@@ -83,6 +100,7 @@ fn sanitize_brand(value: &str) -> Option<String> {
83100

84101
#[cfg(test)]
85102
mod tests {
103+
use bitwarden_vault::FieldType;
86104
use chrono::Month;
87105
use credential_exchange_format::EditableFieldYearMonth;
88106

@@ -150,23 +168,47 @@ mod tests {
150168
full_name: Some("John Doe".to_string().into()),
151169
card_type: Some("Visa".to_string().into()),
152170
verification_number: Some("123".to_string().into()),
153-
pin: None,
171+
pin: Some("4567".to_string().into()),
154172
expiry_date: Some(
155173
EditableFieldYearMonth {
156174
year: 2025,
157175
month: Month::December,
158176
}
159177
.into(),
160178
),
161-
valid_from: None,
179+
valid_from: Some(
180+
EditableFieldYearMonth {
181+
year: 2024,
182+
month: Month::January,
183+
}
184+
.into(),
185+
),
162186
};
163187

164-
let card: Card = (&credit_card).into();
188+
let (card, fields) = to_card(&credit_card);
165189
assert_eq!(card.cardholder_name, Some("John Doe".to_string()));
166190
assert_eq!(card.exp_month, Some("12".to_string()));
167191
assert_eq!(card.exp_year, Some("2025".to_string()));
168192
assert_eq!(card.code, Some("123".to_string()));
169193
assert_eq!(card.brand, Some("Visa".to_string()));
170194
assert_eq!(card.number, Some("4111111111111111".to_string()));
195+
196+
assert_eq!(
197+
fields,
198+
vec![
199+
Field {
200+
name: Some("PIN".to_string()),
201+
value: Some("4567".to_string()),
202+
r#type: FieldType::Hidden as u8,
203+
linked_id: None,
204+
},
205+
Field {
206+
name: Some("Valid From".to_string()),
207+
value: Some("2024-01".to_string()),
208+
r#type: FieldType::Text as u8,
209+
linked_id: None,
210+
},
211+
]
212+
)
171213
}
172214
}

crates/bitwarden-exporters/src/cxf/editable_field.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use bitwarden_vault::FieldType;
22
use credential_exchange_format::{
3-
EditableField, EditableFieldBoolean, EditableFieldConcealedString, EditableFieldDate,
4-
EditableFieldString, EditableFieldWifiNetworkSecurityType,
3+
EditableField, EditableFieldBoolean, EditableFieldConcealedString, EditableFieldCountryCode,
4+
EditableFieldDate, EditableFieldString, EditableFieldWifiNetworkSecurityType,
5+
EditableFieldYearMonth,
56
};
67

78
use crate::Field;
@@ -58,6 +59,14 @@ impl EditableFieldToField for EditableField<EditableFieldWifiNetworkSecurityType
5859
}
5960
}
6061

62+
impl EditableFieldToField for EditableField<EditableFieldCountryCode> {
63+
const FIELD_TYPE: FieldType = FieldType::Text;
64+
65+
fn field_value(&self) -> String {
66+
self.value.0.clone()
67+
}
68+
}
69+
6170
impl EditableFieldToField for EditableField<EditableFieldDate> {
6271
const FIELD_TYPE: FieldType = FieldType::Text;
6372

@@ -66,6 +75,18 @@ impl EditableFieldToField for EditableField<EditableFieldDate> {
6675
}
6776
}
6877

78+
impl EditableFieldToField for EditableField<EditableFieldYearMonth> {
79+
const FIELD_TYPE: FieldType = FieldType::Text;
80+
81+
fn field_value(&self) -> String {
82+
format!(
83+
"{:04}-{:02}",
84+
self.value.year,
85+
self.value.month.number_from_month()
86+
)
87+
}
88+
}
89+
6990
/// Convert WiFi security type enum to human-readable string
7091
fn security_type_to_string(security_type: &EditableFieldWifiNetworkSecurityType) -> &str {
7192
use EditableFieldWifiNetworkSecurityType::*;
@@ -76,6 +97,7 @@ fn security_type_to_string(security_type: &EditableFieldWifiNetworkSecurityType)
7697
Wpa3Personal => "WPA3 Personal",
7798
Wep => "WEP",
7899
Other(s) => s,
100+
_ => "Unknown",
79101
}
80102
}
81103

@@ -248,4 +270,31 @@ mod tests {
248270
}
249271
);
250272
}
273+
274+
#[test]
275+
fn test_create_field_year_month() {
276+
use chrono::Month;
277+
278+
let editable_field = EditableField {
279+
id: None,
280+
label: None,
281+
value: EditableFieldYearMonth {
282+
year: 2025,
283+
month: Month::December,
284+
},
285+
extensions: None,
286+
};
287+
288+
let field = create_field("Card Expiry", &editable_field);
289+
290+
assert_eq!(
291+
field,
292+
Field {
293+
name: Some("Card Expiry".to_string()),
294+
value: Some("2025-12".to_string()),
295+
r#type: FieldType::Text as u8,
296+
linked_id: None,
297+
}
298+
);
299+
}
251300
}

0 commit comments

Comments
 (0)