Skip to content

Commit 1382235

Browse files
authored
[PM-22647] Move build subtitle into cipher types (#307)
`Cipher` is currently non idiomatic rust due to a desire to simplify the conversion layers and to accurately replicate existing behaviour. This has some disadvantages such as `Cipher` having to own all business logic even if it really belongs to one of the cipher types. This PR introduces a `CipherKind` enum which should be implemented by all cipher types. It also introduces a `get_kind` helper on `Cipher` which resolves the accurate `CipherKind` depending on cipher type. This allows us to delegate implementations to the relevant cipher kind avoiding complex match statements in `Cipher`.
1 parent e147b4d commit 1382235

File tree

5 files changed

+284
-255
lines changed

5 files changed

+284
-255
lines changed

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

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
55
#[cfg(feature = "wasm")]
66
use tsify_next::Tsify;
77

8+
use super::cipher::CipherKind;
89
use crate::VaultParseError;
910

1011
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -122,3 +123,114 @@ impl TryFrom<CipherCardModel> for Card {
122123
})
123124
}
124125
}
126+
127+
impl CipherKind for Card {
128+
fn decrypt_subtitle(
129+
&self,
130+
ctx: &mut KeyStoreContext<KeyIds>,
131+
key: SymmetricKeyId,
132+
) -> Result<String, CryptoError> {
133+
let brand = self
134+
.brand
135+
.as_ref()
136+
.map(|b| b.decrypt(ctx, key))
137+
.transpose()?;
138+
let number = self
139+
.number
140+
.as_ref()
141+
.map(|n| n.decrypt(ctx, key))
142+
.transpose()?;
143+
144+
Ok(build_subtitle_card(brand, number))
145+
}
146+
}
147+
148+
/// Builds the subtitle for a card cipher
149+
fn build_subtitle_card(brand: Option<String>, number: Option<String>) -> String {
150+
// Attempt to pre-allocate the string with the expected max-size
151+
let mut subtitle =
152+
String::with_capacity(brand.as_ref().map(|b| b.len()).unwrap_or_default() + 8);
153+
154+
if let Some(brand) = brand {
155+
subtitle.push_str(&brand);
156+
}
157+
158+
if let Some(number) = number {
159+
let number_len = number.len();
160+
if number_len > 4 {
161+
if !subtitle.is_empty() {
162+
subtitle.push_str(", ");
163+
}
164+
165+
// On AMEX cards we show 5 digits instead of 4
166+
let digit_count = match &number[0..2] {
167+
"34" | "37" => 5,
168+
_ => 4,
169+
};
170+
171+
subtitle.push('*');
172+
subtitle.push_str(&number[(number_len - digit_count)..]);
173+
}
174+
}
175+
176+
subtitle
177+
}
178+
179+
#[cfg(test)]
180+
mod tests {
181+
use super::*;
182+
183+
#[test]
184+
fn test_build_subtitle_card_visa() {
185+
let brand = Some("Visa".to_owned());
186+
let number = Some("4111111111111111".to_owned());
187+
188+
let subtitle = build_subtitle_card(brand, number);
189+
assert_eq!(subtitle, "Visa, *1111");
190+
}
191+
192+
#[test]
193+
fn test_build_subtitle_card_mastercard() {
194+
let brand = Some("Mastercard".to_owned());
195+
let number = Some("5555555555554444".to_owned());
196+
197+
let subtitle = build_subtitle_card(brand, number);
198+
assert_eq!(subtitle, "Mastercard, *4444");
199+
}
200+
201+
#[test]
202+
fn test_build_subtitle_card_amex() {
203+
let brand = Some("Amex".to_owned());
204+
let number = Some("378282246310005".to_owned());
205+
206+
let subtitle = build_subtitle_card(brand, number);
207+
assert_eq!(subtitle, "Amex, *10005");
208+
}
209+
210+
#[test]
211+
fn test_build_subtitle_card_underflow() {
212+
let brand = Some("Mastercard".to_owned());
213+
let number = Some("4".to_owned());
214+
215+
let subtitle = build_subtitle_card(brand, number);
216+
assert_eq!(subtitle, "Mastercard");
217+
}
218+
219+
#[test]
220+
fn test_build_subtitle_card_only_brand() {
221+
let brand = Some("Mastercard".to_owned());
222+
let number = None;
223+
224+
let subtitle = build_subtitle_card(brand, number);
225+
assert_eq!(subtitle, "Mastercard");
226+
}
227+
228+
#[test]
229+
fn test_build_subtitle_card_only_card() {
230+
let brand = None;
231+
let number = Some("5555555555554444".to_owned());
232+
233+
let subtitle = build_subtitle_card(brand, number);
234+
assert_eq!(subtitle, "*4444");
235+
}
236+
}

0 commit comments

Comments
 (0)