bitwarden_vault/
password_history.rs1use bitwarden_api_api::models::CipherPasswordHistoryModel;
2use bitwarden_core::key_management::{KeyIds, SymmetricKeyId};
3use bitwarden_crypto::{
4 CompositeEncryptable, CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext,
5 PrimitiveEncryptable,
6};
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9#[cfg(feature = "wasm")]
10use tsify::Tsify;
11
12use crate::VaultParseError;
13
14#[allow(missing_docs)]
15#[derive(Serialize, Deserialize, Debug, Clone)]
16#[serde(rename_all = "camelCase", deny_unknown_fields)]
17#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
18#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
19pub struct PasswordHistory {
20 password: EncString,
21 last_used_date: DateTime<Utc>,
22}
23
24#[allow(missing_docs)]
25#[derive(Serialize, Deserialize, Debug, Clone)]
26#[serde(rename_all = "camelCase", deny_unknown_fields)]
27#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
28#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
29pub struct PasswordHistoryView {
30 pub password: String,
31 pub last_used_date: DateTime<Utc>,
32}
33
34impl IdentifyKey<SymmetricKeyId> for PasswordHistory {
35 fn key_identifier(&self) -> SymmetricKeyId {
36 SymmetricKeyId::User
37 }
38}
39impl IdentifyKey<SymmetricKeyId> for PasswordHistoryView {
40 fn key_identifier(&self) -> SymmetricKeyId {
41 SymmetricKeyId::User
42 }
43}
44
45impl CompositeEncryptable<KeyIds, SymmetricKeyId, PasswordHistory> for PasswordHistoryView {
46 fn encrypt_composite(
47 &self,
48 ctx: &mut KeyStoreContext<KeyIds>,
49 key: SymmetricKeyId,
50 ) -> Result<PasswordHistory, CryptoError> {
51 Ok(PasswordHistory {
52 password: self.password.encrypt(ctx, key)?,
53 last_used_date: self.last_used_date,
54 })
55 }
56}
57
58impl Decryptable<KeyIds, SymmetricKeyId, PasswordHistoryView> for PasswordHistory {
59 fn decrypt(
60 &self,
61 ctx: &mut KeyStoreContext<KeyIds>,
62 key: SymmetricKeyId,
63 ) -> Result<PasswordHistoryView, CryptoError> {
64 Ok(PasswordHistoryView {
65 password: self.password.decrypt(ctx, key).ok().unwrap_or_default(),
66 last_used_date: self.last_used_date,
67 })
68 }
69}
70
71impl TryFrom<CipherPasswordHistoryModel> for PasswordHistory {
72 type Error = VaultParseError;
73
74 fn try_from(model: CipherPasswordHistoryModel) -> Result<Self, Self::Error> {
75 Ok(Self {
76 password: model.password.parse()?,
77 last_used_date: model.last_used_date.parse()?,
78 })
79 }
80}
81
82impl From<PasswordHistory> for CipherPasswordHistoryModel {
83 fn from(history: PasswordHistory) -> Self {
84 Self {
85 password: history.password.to_string(),
86 last_used_date: history.last_used_date.to_rfc3339(),
87 }
88 }
89}
90
91impl PasswordHistoryView {
92 pub(crate) fn new_password(old_password: &str) -> Self {
93 Self {
94 password: old_password.to_string(),
95 last_used_date: Utc::now(),
96 }
97 }
98
99 pub(crate) fn new_field(field_name: &str, old_value: &str) -> Self {
100 Self {
101 password: format!("{field_name}: {old_value}"),
102 last_used_date: Utc::now(),
103 }
104 }
105}