Skip to content

Commit e05ba6e

Browse files
authored
[PM-23646] Add support for CustomFieldsCredential (#412)
Adds support for mapping CustomFieldsCredential to custom fields.
1 parent 49f84e6 commit e05ba6e

File tree

7 files changed

+207
-130
lines changed

7 files changed

+207
-130
lines changed

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,30 @@ use crate::{cxf::editable_field::create_field, Field};
55
/// Convert API key credentials to custom fields
66
pub(super) fn api_key_to_fields(api_key: &ApiKeyCredential) -> Vec<Field> {
77
[
8-
api_key.key.as_ref().map(|key| create_field("API Key", key)),
8+
api_key
9+
.key
10+
.as_ref()
11+
.map(|key| create_field(key, Some("API Key"))),
912
api_key
1013
.username
1114
.as_ref()
12-
.map(|username| create_field("Username", username)),
15+
.map(|username| create_field(username, Some("Username"))),
1316
api_key
1417
.key_type
1518
.as_ref()
16-
.map(|key_type| create_field("Key Type", key_type)),
17-
api_key.url.as_ref().map(|url| create_field("URL", url)),
19+
.map(|key_type| create_field(key_type, Some("Key Type"))),
20+
api_key
21+
.url
22+
.as_ref()
23+
.map(|url| create_field(url, Some("URL"))),
1824
api_key
1925
.valid_from
2026
.as_ref()
21-
.map(|valid_from| create_field("Valid From", valid_from)),
27+
.map(|valid_from| create_field(valid_from, Some("Valid From"))),
2228
api_key
2329
.expiry_date
2430
.as_ref()
25-
.map(|expiry_date| create_field("Expiry Date", expiry_date)),
31+
.map(|expiry_date| create_field(expiry_date, Some("Expiry Date"))),
2632
]
2733
.into_iter()
2834
.flatten()

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,14 @@ pub(super) fn to_card(credential: &CreditCardCredential) -> (Card, Vec<Field>) {
6161
let card = credential.into();
6262

6363
let fields = [
64-
credential.pin.as_ref().map(|v| create_field("PIN", v)),
64+
credential
65+
.pin
66+
.as_ref()
67+
.map(|v| create_field(v, Some("PIN"))),
6568
credential
6669
.valid_from
6770
.as_ref()
68-
.map(|v| create_field("Valid From", v)),
71+
.map(|v| create_field(v, Some("Valid From"))),
6972
]
7073
.into_iter()
7174
.flatten()

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

Lines changed: 103 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,82 +8,105 @@ use credential_exchange_format::{
88
use crate::Field;
99

1010
/// Helper function to create a Field from any EditableField type
11-
pub(super) fn create_field<T>(name: impl Into<String>, field: &T) -> Field
11+
pub(super) fn create_field<T>(field: &T, overridden_name: Option<impl Into<String>>) -> Field
1212
where
1313
T: EditableFieldToField,
1414
{
15+
let field_name = overridden_name
16+
.map(Into::into)
17+
.or_else(|| field.label().clone());
18+
1519
Field {
16-
name: Some(name.into()),
20+
name: field_name,
1721
value: Some(field.field_value()),
1822
r#type: T::FIELD_TYPE as u8,
1923
linked_id: None,
2024
}
2125
}
2226

23-
/// Trait to convert CXP EditableField types to Bitwarden Field values and types
24-
pub(super) trait EditableFieldToField {
27+
/// Trait to define field type and value conversion for inner field types
28+
pub(super) trait InnerFieldType {
2529
const FIELD_TYPE: FieldType;
2630

27-
fn field_value(&self) -> String;
31+
fn to_field_value(&self) -> String;
2832
}
2933

30-
impl EditableFieldToField for EditableField<EditableFieldString> {
34+
impl InnerFieldType for EditableFieldString {
3135
const FIELD_TYPE: FieldType = FieldType::Text;
3236

33-
fn field_value(&self) -> String {
34-
self.value.0.clone()
37+
fn to_field_value(&self) -> String {
38+
self.0.clone()
3539
}
3640
}
3741

38-
impl EditableFieldToField for EditableField<EditableFieldConcealedString> {
42+
impl InnerFieldType for EditableFieldConcealedString {
3943
const FIELD_TYPE: FieldType = FieldType::Hidden;
4044

41-
fn field_value(&self) -> String {
42-
self.value.0.clone()
45+
fn to_field_value(&self) -> String {
46+
self.0.clone()
4347
}
4448
}
4549

46-
impl EditableFieldToField for EditableField<EditableFieldBoolean> {
50+
impl InnerFieldType for EditableFieldBoolean {
4751
const FIELD_TYPE: FieldType = FieldType::Boolean;
4852

49-
fn field_value(&self) -> String {
50-
self.value.0.to_string()
53+
fn to_field_value(&self) -> String {
54+
self.0.to_string()
5155
}
5256
}
5357

54-
impl EditableFieldToField for EditableField<EditableFieldWifiNetworkSecurityType> {
58+
impl InnerFieldType for EditableFieldWifiNetworkSecurityType {
5559
const FIELD_TYPE: FieldType = FieldType::Text;
5660

57-
fn field_value(&self) -> String {
58-
security_type_to_string(&self.value).to_string()
61+
fn to_field_value(&self) -> String {
62+
security_type_to_string(self).to_string()
5963
}
6064
}
6165

62-
impl EditableFieldToField for EditableField<EditableFieldCountryCode> {
66+
impl InnerFieldType for EditableFieldCountryCode {
6367
const FIELD_TYPE: FieldType = FieldType::Text;
6468

65-
fn field_value(&self) -> String {
66-
self.value.0.clone()
69+
fn to_field_value(&self) -> String {
70+
self.0.clone()
6771
}
6872
}
6973

70-
impl EditableFieldToField for EditableField<EditableFieldDate> {
74+
impl InnerFieldType for EditableFieldDate {
7175
const FIELD_TYPE: FieldType = FieldType::Text;
7276

73-
fn field_value(&self) -> String {
74-
self.value.0.to_string()
77+
fn to_field_value(&self) -> String {
78+
self.0.to_string()
7579
}
7680
}
7781

78-
impl EditableFieldToField for EditableField<EditableFieldYearMonth> {
82+
impl InnerFieldType for EditableFieldYearMonth {
7983
const FIELD_TYPE: FieldType = FieldType::Text;
8084

85+
fn to_field_value(&self) -> String {
86+
format!("{:04}-{:02}", self.year, self.month.number_from_month())
87+
}
88+
}
89+
90+
/// Trait to convert CXP EditableField types to Bitwarden Field values and types
91+
pub(super) trait EditableFieldToField {
92+
const FIELD_TYPE: FieldType;
93+
94+
fn field_value(&self) -> String;
95+
fn label(&self) -> &Option<String>;
96+
}
97+
98+
impl<T> EditableFieldToField for EditableField<T>
99+
where
100+
T: InnerFieldType,
101+
{
102+
const FIELD_TYPE: FieldType = T::FIELD_TYPE;
103+
81104
fn field_value(&self) -> String {
82-
format!(
83-
"{:04}-{:02}",
84-
self.value.year,
85-
self.value.month.number_from_month()
86-
)
105+
self.value.to_field_value()
106+
}
107+
108+
fn label(&self) -> &Option<String> {
109+
&self.label
87110
}
88111
}
89112

@@ -114,7 +137,7 @@ mod tests {
114137
extensions: None,
115138
};
116139

117-
let field = create_field("Test Name", &editable_field);
140+
let field = create_field(&editable_field, Some("Test Name"));
118141

119142
assert_eq!(
120143
field,
@@ -136,7 +159,7 @@ mod tests {
136159
extensions: None,
137160
};
138161

139-
let field = create_field("Password", &editable_field);
162+
let field = create_field(&editable_field, Some("Password"));
140163

141164
assert_eq!(
142165
field,
@@ -158,7 +181,7 @@ mod tests {
158181
extensions: None,
159182
};
160183

161-
let field = create_field("Is Enabled", &editable_field);
184+
let field = create_field(&editable_field, Some("Is Enabled"));
162185

163186
assert_eq!(
164187
field,
@@ -180,7 +203,7 @@ mod tests {
180203
extensions: None,
181204
};
182205

183-
let field = create_field("Is Hidden", &editable_field);
206+
let field = create_field(&editable_field, Some("Is Hidden"));
184207

185208
assert_eq!(
186209
field,
@@ -202,7 +225,7 @@ mod tests {
202225
extensions: None,
203226
};
204227

205-
let field = create_field("WiFi Security", &editable_field);
228+
let field = create_field(&editable_field, Some("WiFi Security"));
206229

207230
assert_eq!(
208231
field,
@@ -258,7 +281,7 @@ mod tests {
258281
extensions: None,
259282
};
260283

261-
let field = create_field("Expiry Date".to_string(), &editable_field);
284+
let field = create_field(&editable_field, Some("Expiry Date".to_string()));
262285

263286
assert_eq!(
264287
field,
@@ -285,7 +308,7 @@ mod tests {
285308
extensions: None,
286309
};
287310

288-
let field = create_field("Card Expiry", &editable_field);
311+
let field = create_field(&editable_field, Some("Card Expiry"));
289312

290313
assert_eq!(
291314
field,
@@ -297,4 +320,48 @@ mod tests {
297320
}
298321
);
299322
}
323+
324+
#[test]
325+
fn test_create_field_with_none_name_uses_label() {
326+
let editable_field = EditableField {
327+
id: None,
328+
label: Some("Label From Field".to_string()),
329+
value: EditableFieldString("Test Value".to_string()),
330+
extensions: None,
331+
};
332+
333+
let field = create_field(&editable_field, None::<String>);
334+
335+
assert_eq!(
336+
field,
337+
Field {
338+
name: Some("Label From Field".to_string()),
339+
value: Some("Test Value".to_string()),
340+
r#type: FieldType::Text as u8,
341+
linked_id: None,
342+
}
343+
);
344+
}
345+
346+
#[test]
347+
fn test_create_field_with_none_name_and_none_label() {
348+
let editable_field = EditableField {
349+
id: None,
350+
label: None,
351+
value: EditableFieldString("Test Value".to_string()),
352+
extensions: None,
353+
};
354+
355+
let field = create_field(&editable_field, None::<String>);
356+
357+
assert_eq!(
358+
field,
359+
Field {
360+
name: None,
361+
value: Some("Test Value".to_string()),
362+
r#type: FieldType::Text as u8,
363+
linked_id: None,
364+
}
365+
);
366+
}
300367
}

0 commit comments

Comments
 (0)