autofill/db/
store.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::db::models::address::{Address, UpdatableAddressFields};
6use crate::db::models::credit_card::{CreditCard, UpdatableCreditCardFields};
7use crate::db::models::passport::{Passport, UpdatablePassportFields};
8use crate::db::{
9    addresses, credit_cards, credit_cards::CreditCardsDeletionMetrics, passports, AutofillDb,
10};
11use crate::error::*;
12use error_support::handle_error;
13use rusqlite::{
14    types::{FromSql, ToSql},
15    Connection,
16};
17use sql_support::{self, run_maintenance, ConnExt};
18use std::path::Path;
19use std::sync::{Arc, Mutex, Weak};
20use sync15::engine::{SyncEngine, SyncEngineId};
21use sync_guid::Guid;
22
23// Our "sync manager" will use whatever is stashed here.
24lazy_static::lazy_static! {
25    // Mutex: just taken long enough to update the contents - needed to wrap
26    //        the Weak as it isn't `Sync`
27    // [Arc/Weak]<Store>: What the sync manager actually needs.
28    static ref STORE_FOR_MANAGER: Mutex<Weak<Store>> = Mutex::new(Weak::new());
29}
30
31/// Called by the sync manager to get a sync engine via the store previously
32/// registered with the sync manager.
33pub fn get_registered_sync_engine(engine_id: &SyncEngineId) -> Option<Box<dyn SyncEngine>> {
34    let weak = STORE_FOR_MANAGER.lock().unwrap();
35    match weak.upgrade() {
36        None => None,
37        Some(store) => match engine_id {
38            SyncEngineId::Addresses => Some(Box::new(crate::sync::address::create_engine(store))),
39            SyncEngineId::CreditCards => {
40                Some(Box::new(crate::sync::credit_card::create_engine(store)))
41            }
42            // panicking here seems reasonable - it's a static error if this
43            // it hit, not something that runtime conditions can influence.
44            _ => unreachable!("can't provide unknown engine: {}", engine_id),
45        },
46    }
47}
48
49// This is the type that uniffi exposes.
50pub struct Store {
51    pub(crate) db: Mutex<AutofillDb>,
52}
53
54impl Store {
55    #[handle_error(Error)]
56    pub fn new(db_path: impl AsRef<Path>) -> ApiResult<Self> {
57        Ok(Self {
58            db: Mutex::new(AutofillDb::new(db_path)?),
59        })
60    }
61
62    /// Creates a store backed by an in-memory database with its own memory API (required for unit tests).
63    #[cfg(test)]
64    pub fn new_memory() -> Self {
65        Self {
66            db: Mutex::new(crate::db::test::new_mem_db()),
67        }
68    }
69
70    /// Creates a store backed by an in-memory database that shares its memory API (required for autofill sync tests).
71    #[handle_error(Error)]
72    pub fn new_shared_memory(db_name: &str) -> ApiResult<Self> {
73        Ok(Self {
74            db: Mutex::new(AutofillDb::new_memory(db_name)?),
75        })
76    }
77
78    #[handle_error(Error)]
79    pub fn add_credit_card(&self, fields: UpdatableCreditCardFields) -> ApiResult<CreditCard> {
80        let credit_card = credit_cards::add_credit_card(&self.db.lock().unwrap().writer, fields)?;
81        Ok(credit_card.into())
82    }
83
84    #[handle_error(Error)]
85    pub fn get_credit_card(&self, guid: String) -> ApiResult<CreditCard> {
86        let credit_card =
87            credit_cards::get_credit_card(&self.db.lock().unwrap().writer, &Guid::new(&guid))?;
88        Ok(credit_card.into())
89    }
90
91    #[handle_error(Error)]
92    pub fn get_all_credit_cards(&self) -> ApiResult<Vec<CreditCard>> {
93        let credit_cards = credit_cards::get_all_credit_cards(&self.db.lock().unwrap().writer)?
94            .into_iter()
95            .map(|x| x.into())
96            .collect();
97        Ok(credit_cards)
98    }
99
100    #[handle_error(Error)]
101    pub fn count_all_credit_cards(&self) -> ApiResult<i64> {
102        let count = credit_cards::count_all_credit_cards(&self.db.lock().unwrap().writer)?;
103        Ok(count)
104    }
105
106    #[handle_error(Error)]
107    pub fn update_credit_card(
108        &self,
109        guid: String,
110        credit_card: UpdatableCreditCardFields,
111    ) -> ApiResult<()> {
112        credit_cards::update_credit_card(
113            &self.db.lock().unwrap().writer,
114            &Guid::new(&guid),
115            &credit_card,
116        )
117    }
118
119    #[handle_error(Error)]
120    pub fn delete_credit_card(&self, guid: String) -> ApiResult<bool> {
121        credit_cards::delete_credit_card(&self.db.lock().unwrap().writer, &Guid::new(&guid))
122    }
123
124    #[handle_error(Error)]
125    pub fn touch_credit_card(&self, guid: String) -> ApiResult<()> {
126        credit_cards::touch(&self.db.lock().unwrap().writer, &Guid::new(&guid))
127    }
128
129    #[handle_error(Error)]
130    pub fn add_address(&self, new_address: UpdatableAddressFields) -> ApiResult<Address> {
131        Ok(addresses::add_address(&self.db.lock().unwrap().writer, new_address)?.into())
132    }
133
134    #[handle_error(Error)]
135    pub fn get_address(&self, guid: String) -> ApiResult<Address> {
136        Ok(addresses::get_address(&self.db.lock().unwrap().writer, &Guid::new(&guid))?.into())
137    }
138
139    #[handle_error(Error)]
140    pub fn get_all_addresses(&self) -> ApiResult<Vec<Address>> {
141        let addresses = addresses::get_all_addresses(&self.db.lock().unwrap().writer)?
142            .into_iter()
143            .map(|x| x.into())
144            .collect();
145        Ok(addresses)
146    }
147
148    #[handle_error(Error)]
149    pub fn count_all_addresses(&self) -> ApiResult<i64> {
150        let count = addresses::count_all_addresses(&self.db.lock().unwrap().writer)?;
151        Ok(count)
152    }
153
154    #[handle_error(Error)]
155    pub fn update_address(&self, guid: String, address: UpdatableAddressFields) -> ApiResult<()> {
156        addresses::update_address(&self.db.lock().unwrap().writer, &Guid::new(&guid), &address)
157    }
158
159    #[handle_error(Error)]
160    pub fn delete_address(&self, guid: String) -> ApiResult<bool> {
161        addresses::delete_address(&self.db.lock().unwrap().writer, &Guid::new(&guid))
162    }
163
164    #[handle_error(Error)]
165    pub fn touch_address(&self, guid: String) -> ApiResult<()> {
166        addresses::touch(&self.db.lock().unwrap().writer, &Guid::new(&guid))
167    }
168
169    #[handle_error(Error)]
170    pub fn add_passport(&self, fields: UpdatablePassportFields) -> ApiResult<Passport> {
171        Ok(passports::add_passport(&self.db.lock().unwrap().writer, fields)?.into())
172    }
173
174    #[handle_error(Error)]
175    pub fn get_passport(&self, guid: String) -> ApiResult<Passport> {
176        Ok(passports::get_passport(&self.db.lock().unwrap().writer, &Guid::new(&guid))?.into())
177    }
178
179    #[handle_error(Error)]
180    pub fn get_all_passports(&self) -> ApiResult<Vec<Passport>> {
181        let passports = passports::get_all_passports(&self.db.lock().unwrap().writer)?
182            .into_iter()
183            .map(|x| x.into())
184            .collect();
185        Ok(passports)
186    }
187
188    #[handle_error(Error)]
189    pub fn count_all_passports(&self) -> ApiResult<i64> {
190        passports::count_all_passports(&self.db.lock().unwrap().writer)
191    }
192
193    #[handle_error(Error)]
194    pub fn update_passport(
195        &self,
196        guid: String,
197        passport: UpdatablePassportFields,
198    ) -> ApiResult<()> {
199        passports::update_passport(
200            &self.db.lock().unwrap().writer,
201            &Guid::new(&guid),
202            &passport,
203        )
204    }
205
206    #[handle_error(Error)]
207    pub fn delete_passport(&self, guid: String) -> ApiResult<bool> {
208        passports::delete_passport(&self.db.lock().unwrap().writer, &Guid::new(&guid))
209    }
210
211    #[handle_error(Error)]
212    pub fn touch_passport(&self, guid: String) -> ApiResult<()> {
213        passports::touch(&self.db.lock().unwrap().writer, &Guid::new(&guid))
214    }
215
216    #[handle_error(Error)]
217    pub fn scrub_encrypted_data(self: Arc<Self>) -> ApiResult<()> {
218        // scrub the data on disk
219        // Currently only credit cards have encrypted data
220        credit_cards::scrub_encrypted_credit_card_data(&self.db.lock().unwrap().writer)?;
221        // Force the sync engine to refetch data (only need to do this for the credit cards, since the
222        // addresses engine doesn't store encrypted data).
223        crate::sync::credit_card::create_engine(self).reset_local_sync_data()?;
224        Ok(())
225    }
226
227    #[handle_error(Error)]
228    pub fn scrub_undecryptable_credit_card_data_for_remote_replacement(
229        self: Arc<Self>,
230        local_encryption_key: String,
231    ) -> ApiResult<CreditCardsDeletionMetrics> {
232        let db = &self.db.lock().unwrap().writer;
233        let deletion_stats =
234            credit_cards::scrub_undecryptable_credit_card_data_for_remote_replacement(
235                db,
236                local_encryption_key,
237            )?;
238
239        // Here we reset the local sync data so that the credit card engine syncs as if
240        // it were the first sync. This will potentially allow a previous sync of the
241        // record that exists on the sync server to overwrite the local record and restore
242        // the scrubbed credit card number.
243        crate::sync::credit_card::create_engine(self.clone())
244            .reset_local_sync_data_for_verification(db)?;
245        Ok(deletion_stats)
246    }
247
248    #[handle_error(Error)]
249    pub fn run_maintenance(&self) -> ApiResult<()> {
250        let conn = self.db.lock().unwrap();
251        run_maintenance(&conn)?;
252        Ok(())
253    }
254
255    // This allows the embedding app to say "make this instance available to
256    // the sync manager". The implementation is more like "offer to sync mgr"
257    // (thereby avoiding us needing to link with the sync manager) but
258    // `register_with_sync_manager()` is logically what's happening so that's
259    // the name it gets.
260    pub fn register_with_sync_manager(self: Arc<Self>) {
261        let mut state = STORE_FOR_MANAGER.lock().unwrap();
262        *state = Arc::downgrade(&self);
263    }
264
265    // These 2 are a little odd - they aren't exposed by uniffi - currently the
266    // only consumer of this is our "example" (and hence why they
267    // are `pub` and not `pub(crate)`).
268    // We could probably make the example work with the sync manager - but then
269    // our example would link with places and logins etc, and it's not a big
270    // deal really.
271    pub fn create_credit_cards_sync_engine(self: Arc<Self>) -> Box<dyn SyncEngine> {
272        Box::new(crate::sync::credit_card::create_engine(self))
273    }
274
275    pub fn create_addresses_sync_engine(self: Arc<Self>) -> Box<dyn SyncEngine> {
276        Box::new(crate::sync::address::create_engine(self))
277    }
278}
279
280pub(crate) fn put_meta(conn: &Connection, key: &str, value: &dyn ToSql) -> Result<()> {
281    conn.execute_cached(
282        "REPLACE INTO moz_meta (key, value) VALUES (:key, :value)",
283        &[(":key", &key as &dyn ToSql), (":value", value)],
284    )?;
285    Ok(())
286}
287
288pub(crate) fn get_meta<T: FromSql>(conn: &Connection, key: &str) -> Result<Option<T>> {
289    let res = conn.try_query_one(
290        "SELECT value FROM moz_meta WHERE key = :key",
291        &[(":key", &key)],
292        true,
293    )?;
294    Ok(res)
295}
296
297pub(crate) fn delete_meta(conn: &Connection, key: &str) -> Result<()> {
298    conn.execute_cached("DELETE FROM moz_meta WHERE key = :key", &[(":key", &key)])?;
299    Ok(())
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305    use crate::db::test::new_mem_db;
306    use crate::encryption::EncryptorDecryptor;
307    use nss_as::ensure_initialized;
308
309    #[test]
310    fn test_autofill_meta() -> Result<()> {
311        let db = new_mem_db();
312        let test_key = "TEST KEY A";
313        let test_value = "TEST VALUE A";
314        let test_key2 = "TEST KEY B";
315        let test_value2 = "TEST VALUE B";
316
317        put_meta(&db, test_key, &test_value)?;
318        put_meta(&db, test_key2, &test_value2)?;
319
320        let retrieved_value: String = get_meta(&db, test_key)?.expect("test value");
321        let retrieved_value2: String = get_meta(&db, test_key2)?.expect("test value 2");
322
323        assert_eq!(retrieved_value, test_value);
324        assert_eq!(retrieved_value2, test_value2);
325
326        // check that the value of an existing key can be updated
327        let test_value3 = "TEST VALUE C";
328        put_meta(&db, test_key, &test_value3)?;
329
330        let retrieved_value3: String = get_meta(&db, test_key)?.expect("test value 3");
331
332        assert_eq!(retrieved_value3, test_value3);
333
334        // check that a deleted key is not retrieved
335        delete_meta(&db, test_key)?;
336        let retrieved_value4: Option<String> = get_meta(&db, test_key)?;
337        assert!(retrieved_value4.is_none());
338
339        db.writer.execute("DELETE FROM moz_meta", [])?;
340
341        Ok(())
342    }
343
344    #[test]
345    fn test_sync_manager_registration() {
346        let store = Arc::new(Store::new_shared_memory("sync-mgr-test").unwrap());
347        assert_eq!(Arc::strong_count(&store), 1);
348        assert_eq!(Arc::weak_count(&store), 0);
349        Arc::clone(&store).register_with_sync_manager();
350        assert_eq!(Arc::strong_count(&store), 1);
351        assert_eq!(Arc::weak_count(&store), 1);
352        let registered = STORE_FOR_MANAGER
353            .lock()
354            .unwrap()
355            .upgrade()
356            .expect("should upgrade");
357        assert!(Arc::ptr_eq(&store, &registered));
358        drop(registered);
359        // should be no new references
360        assert_eq!(Arc::strong_count(&store), 1);
361        assert_eq!(Arc::weak_count(&store), 1);
362        // dropping the registered object should drop the registration.
363        drop(store);
364        assert!(STORE_FOR_MANAGER.lock().unwrap().upgrade().is_none());
365    }
366
367    #[test]
368    fn test_scrub_undecryptable_credit_card_data_for_remote_replacement() {
369        ensure_initialized();
370        let store = Arc::new(Store::new_shared_memory("sync-mgr-test").expect("create store"));
371        let key = EncryptorDecryptor::create_key().expect("create key");
372        let encdec = EncryptorDecryptor::new(&key).expect("create EncryptorDecryptor");
373
374        store
375            .add_credit_card(UpdatableCreditCardFields {
376                cc_name: "john deer".to_string(),
377                cc_number_enc: encdec
378                    .encrypt("567812345678123456781")
379                    .expect("encrypt cc number"),
380                cc_number_last_4: "6781".to_string(),
381                cc_exp_month: 10,
382                cc_exp_year: 2025,
383                cc_type: "mastercard".to_string(),
384            })
385            .expect("add credit card to database");
386
387        store
388            .scrub_undecryptable_credit_card_data_for_remote_replacement(key)
389            .expect("scrub credit card record");
390    }
391}