1use bitwarden_api_api::models::CipherSecureNoteModel;
2use bitwarden_core::{
3 key_management::{KeyIds, SymmetricKeyId},
4 require,
5};
6use bitwarden_crypto::{CompositeEncryptable, CryptoError, Decryptable, KeyStoreContext};
7use serde::{Deserialize, Deserializer, Serialize};
8use serde_repr::Serialize_repr;
9#[cfg(feature = "wasm")]
10use tsify::Tsify;
11#[cfg(feature = "wasm")]
12use wasm_bindgen::prelude::wasm_bindgen;
13
14use crate::{
15 Cipher, VaultParseError,
16 cipher::cipher::{CipherKind, CopyableCipherFields},
17};
18
19#[allow(missing_docs)]
20#[derive(Clone, Copy, Serialize_repr, Debug)]
21#[repr(u8)]
22#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
23#[cfg_attr(feature = "wasm", wasm_bindgen)]
24pub enum SecureNoteType {
25 Generic = 0,
26}
27
28#[derive(Clone, Serialize, Deserialize, Debug)]
29#[serde(rename_all = "camelCase")]
30#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
31#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
32pub struct SecureNote {
33 r#type: SecureNoteType,
34}
35
36impl<'de> Deserialize<'de> for SecureNoteType {
39 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
40 where
41 D: Deserializer<'de>,
42 {
43 match u8::deserialize(deserializer) {
44 Ok(0) => Ok(SecureNoteType::Generic),
45 Ok(_) => {
46 Ok(SecureNoteType::Generic)
48 }
49 Err(_) => Ok(SecureNoteType::Generic),
50 }
51 }
52}
53
54#[allow(missing_docs)]
55#[derive(Clone, Serialize, Deserialize, Debug)]
56#[serde(rename_all = "camelCase", deny_unknown_fields)]
57#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
58#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
59pub struct SecureNoteView {
60 pub r#type: SecureNoteType,
61}
62
63impl CompositeEncryptable<KeyIds, SymmetricKeyId, SecureNote> for SecureNoteView {
64 fn encrypt_composite(
65 &self,
66 _ctx: &mut KeyStoreContext<KeyIds>,
67 _key: SymmetricKeyId,
68 ) -> Result<SecureNote, CryptoError> {
69 Ok(SecureNote {
70 r#type: self.r#type,
71 })
72 }
73}
74
75impl Decryptable<KeyIds, SymmetricKeyId, SecureNoteView> for SecureNote {
76 fn decrypt(
77 &self,
78 _ctx: &mut KeyStoreContext<KeyIds>,
79 _key: SymmetricKeyId,
80 ) -> Result<SecureNoteView, CryptoError> {
81 Ok(SecureNoteView {
82 r#type: self.r#type,
83 })
84 }
85}
86
87impl TryFrom<CipherSecureNoteModel> for SecureNote {
88 type Error = VaultParseError;
89
90 fn try_from(model: CipherSecureNoteModel) -> Result<Self, Self::Error> {
91 Ok(Self {
92 r#type: require!(model.r#type).try_into()?,
93 })
94 }
95}
96
97impl TryFrom<bitwarden_api_api::models::SecureNoteType> for SecureNoteType {
98 type Error = bitwarden_core::MissingFieldError;
99
100 fn try_from(model: bitwarden_api_api::models::SecureNoteType) -> Result<Self, Self::Error> {
101 Ok(match model {
102 bitwarden_api_api::models::SecureNoteType::Generic => SecureNoteType::Generic,
103 bitwarden_api_api::models::SecureNoteType::__Unknown(_) => {
104 return Err(bitwarden_core::MissingFieldError("type"));
105 }
106 })
107 }
108}
109
110impl From<SecureNoteType> for bitwarden_api_api::models::SecureNoteType {
111 fn from(model: SecureNoteType) -> Self {
112 match model {
113 SecureNoteType::Generic => bitwarden_api_api::models::SecureNoteType::Generic,
114 }
115 }
116}
117
118impl From<SecureNote> for CipherSecureNoteModel {
119 fn from(model: SecureNote) -> Self {
120 Self {
121 r#type: Some(model.r#type.into()),
122 }
123 }
124}
125
126impl CipherKind for SecureNote {
127 fn get_copyable_fields(&self, cipher: Option<&Cipher>) -> Vec<CopyableCipherFields> {
128 [cipher
129 .and_then(|c| c.notes.as_ref())
130 .map(|_| CopyableCipherFields::SecureNotes)]
131 .into_iter()
132 .flatten()
133 .collect()
134 }
135
136 fn decrypt_subtitle(
137 &self,
138 _ctx: &mut KeyStoreContext<KeyIds>,
139 _key: SymmetricKeyId,
140 ) -> Result<String, CryptoError> {
141 Ok(String::new())
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use crate::{
148 CipherRepromptType, CipherType, SecureNoteType,
149 cipher::cipher::{Cipher, CipherKind, CopyableCipherFields},
150 secure_note::SecureNote,
151 };
152
153 fn create_cipher_for_note(note: SecureNote) -> Cipher {
154 Cipher {
155 id: Some("090c19ea-a61a-4df6-8963-262b97bc6266".parse().unwrap()),
156 organization_id: None,
157 folder_id: None,
158 collection_ids: vec![],
159 r#type: CipherType::Login,
160 key: None,
161 name: "2.iovOJUb186UXu+0AlQggjw==|LeWZhrT0B7rqFtDufOJMlJsftwmMGuaoBxf/Cig4D4A9XHhUqacd8uOYP7M5bd/k|++gmrHIyt8hvvPP9dwFS/CGd+POfzmeXzKOsuyJpDDc=".parse().unwrap(),
162 notes: None,
163 login: None,
164 identity: None,
165 card: None,
166 secure_note: Some(note),
167 ssh_key: None,
168 favorite: false,
169 reprompt: CipherRepromptType::None,
170 organization_use_totp: false,
171 edit: true,
172 permissions: None,
173 view_password: true,
174 local_data: None,
175 attachments: None,
176 fields: None,
177 password_history: None,
178 creation_date: "2024-01-01T00:00:00.000Z".parse().unwrap(),
179 deleted_date: None,
180 revision_date: "2024-01-01T00:00:00.000Z".parse().unwrap(),
181 archived_date: None,
182 data: None,
183 }
184 }
185
186 #[test]
187 fn test_get_copyable_fields_secure_note_empty() {
188 let secure_note = SecureNote {
189 r#type: SecureNoteType::Generic,
190 };
191
192 let cipher = create_cipher_for_note(secure_note.clone());
193
194 let copyable_fields = secure_note.get_copyable_fields(Some(&cipher));
195 assert_eq!(copyable_fields, vec![]);
196 }
197
198 #[test]
199 fn test_get_copyable_fields_secure_note_has_notes() {
200 let secure_note = SecureNote {
201 r#type: SecureNoteType::Generic,
202 };
203
204 let mut cipher = create_cipher_for_note(secure_note.clone());
205 cipher.notes = Some("2.iovOJUb186UXu+0AlQggjw==|LeWZhrT0B7rqFtDufOJMlJsftwmMGuaoBxf/Cig4D4A9XHhUqacd8uOYP7M5bd/k|++gmrHIyt8hvvPP9dwFS/CGd+POfzmeXzKOsuyJpDDc=".parse().unwrap());
206
207 let copyable_fields = secure_note.get_copyable_fields(Some(&cipher));
208 assert_eq!(copyable_fields, vec![CopyableCipherFields::SecureNotes]);
209 }
210
211 #[test]
212 fn test_get_copyable_fields_secure_no_cipher() {
213 let secure_note = SecureNote {
214 r#type: SecureNoteType::Generic,
215 };
216
217 let copyable_fields = secure_note.get_copyable_fields(None);
218 assert_eq!(copyable_fields, vec![]);
219 }
220}