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::{
6    Address, AddressBulkResultEntry, AddressBulkTombstoneResultEntry, AddressTombstone,
7    UpdatableAddressFields, UpdatableAddressFieldsWithMeta,
8};
9use crate::db::models::credit_card::{
10    CreditCard, CreditCardBulkResultEntry, CreditCardBulkTombstoneResultEntry, CreditCardTombstone,
11    UpdatableCreditCardFields, UpdatableCreditCardFieldsWithMeta,
12};
13use crate::db::models::passport::{Passport, UpdatablePassportFields};
14use crate::db::{
15    addresses, credit_cards, credit_cards::CreditCardsDeletionMetrics, passports, AutofillDb,
16};
17use crate::error::*;
18use error_support::handle_error;
19use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
20use rusqlite::{
21    types::{FromSql, ToSql},
22    Connection,
23};
24use sql_support::{self, run_maintenance, ConnExt};
25use std::path::Path;
26use std::sync::{Arc, Weak};
27use sync15::engine::{SyncEngine, SyncEngineId};
28use sync_guid::Guid;
29
30// Our "sync manager" will use whatever is stashed here.
31lazy_static::lazy_static! {
32    // Mutex: just taken long enough to update the contents - needed to wrap
33    //        the Weak as it isn't `Sync`
34    // [Arc/Weak]<Store>: What the sync manager actually needs.
35    static ref STORE_FOR_MANAGER: std::sync::Mutex<Weak<Store>> =
36        std::sync::Mutex::new(Weak::new());
37}
38
39/// Called by the sync manager to get a sync engine via the store previously
40/// registered with the sync manager.
41pub fn get_registered_sync_engine(engine_id: &SyncEngineId) -> Option<Box<dyn SyncEngine>> {
42    let weak = STORE_FOR_MANAGER.lock().unwrap();
43    match weak.upgrade() {
44        None => None,
45        Some(store) => match engine_id {
46            SyncEngineId::Addresses => Some(Box::new(crate::sync::address::create_engine(store))),
47            SyncEngineId::CreditCards => {
48                Some(Box::new(crate::sync::credit_card::create_engine(store)))
49            }
50            // panicking here seems reasonable - it's a static error if this
51            // it hit, not something that runtime conditions can influence.
52            _ => unreachable!("can't provide unknown engine: {}", engine_id),
53        },
54    }
55}
56
57// This is the type that uniffi exposes.
58pub struct Store {
59    pub(crate) db: Mutex<Option<AutofillDb>>,
60}
61
62impl Store {
63    #[handle_error(Error)]
64    pub fn new(db_path: impl AsRef<Path>) -> ApiResult<Self> {
65        Ok(Self {
66            db: Mutex::new(Some(AutofillDb::new(db_path)?)),
67        })
68    }
69
70    /// Creates a store backed by an in-memory database with its own memory API (required for unit tests).
71    #[cfg(test)]
72    pub fn new_memory() -> Self {
73        Self {
74            db: Mutex::new(Some(crate::db::test::new_mem_db())),
75        }
76    }
77
78    /// Creates a store backed by an in-memory database that shares its memory API (required for autofill sync tests).
79    #[handle_error(Error)]
80    pub fn new_shared_memory(db_name: &str) -> ApiResult<Self> {
81        Ok(Self {
82            db: Mutex::new(Some(AutofillDb::new_memory(db_name)?)),
83        })
84    }
85
86    pub(crate) fn lock_db(&self) -> Result<MappedMutexGuard<'_, AutofillDb>> {
87        MutexGuard::try_map(self.db.lock(), |db| db.as_mut()).map_err(|_| Error::DatabaseClosed)
88    }
89
90    #[handle_error(Error)]
91    pub fn add_credit_card(&self, fields: UpdatableCreditCardFields) -> ApiResult<CreditCard> {
92        let credit_card = credit_cards::add_credit_card(&self.lock_db()?.writer, fields)?;
93        Ok(credit_card.into())
94    }
95
96    /// Adds a credit card **including metadata**. Normally the metadata (guid,
97    /// timestamps, sync change counter) is owned by this store and
98    /// `add_credit_card` will take care of it. This is for the case where a
99    /// record is being migrated from another store that already holds it.
100    #[handle_error(Error)]
101    pub fn add_credit_card_with_meta(
102        &self,
103        entry_with_meta: UpdatableCreditCardFieldsWithMeta,
104    ) -> ApiResult<CreditCard> {
105        Ok(credit_cards::add_credit_card_with_meta(
106            &self.lock_db()?.writer,
107            entry_with_meta.fields,
108            entry_with_meta.meta,
109        )?
110        .into())
111    }
112
113    /// Adds multiple credit cards **including metadata**, with a result per
114    /// record.
115    #[handle_error(Error)]
116    pub fn add_many_credit_cards_with_meta(
117        &self,
118        entries_with_meta: Vec<UpdatableCreditCardFieldsWithMeta>,
119    ) -> ApiResult<Vec<CreditCardBulkResultEntry>> {
120        let results = credit_cards::add_many_credit_cards_with_meta(
121            &self.lock_db()?.writer,
122            entries_with_meta,
123        )?;
124        Ok(results
125            .into_iter()
126            .map(|result| match result {
127                Ok(credit_card) => CreditCardBulkResultEntry::Success {
128                    credit_card: credit_card.into(),
129                },
130                Err(message) => CreditCardBulkResultEntry::Error { message },
131            })
132            .collect())
133    }
134
135    /// Adds tombstones for credit cards whose deletion has not yet been
136    /// uploaded, with a result per record.
137    #[handle_error(Error)]
138    pub fn add_many_credit_card_tombstones(
139        &self,
140        tombstones: Vec<CreditCardTombstone>,
141    ) -> ApiResult<Vec<CreditCardBulkTombstoneResultEntry>> {
142        let results = credit_cards::add_many_credit_card_tombstones(
143            &self.lock_db()?.writer,
144            tombstones
145                .into_iter()
146                .map(|t| (t.guid, t.time_deleted))
147                .collect(),
148        )?;
149        Ok(results
150            .into_iter()
151            .map(|result| match result {
152                Ok(guid) => CreditCardBulkTombstoneResultEntry::Success { guid },
153                Err(message) => CreditCardBulkTombstoneResultEntry::Error { message },
154            })
155            .collect())
156    }
157
158    /// Removes every credit card and every credit card tombstone.
159    ///
160    /// A migration primitive: it leaves the sync mirror intact and produces no
161    /// tombstones, so the deletions are never uploaded and a synced profile gets
162    /// the records back on the next sync. Use `delete_credit_card` to delete on the
163    /// user's behalf.
164    #[handle_error(Error)]
165    pub fn delete_all_credit_cards(&self) -> ApiResult<()> {
166        credit_cards::delete_all_credit_cards(&self.lock_db()?.writer)?;
167        Ok(())
168    }
169
170    /// Updates a credit card **including metadata**, setting both its fields
171    /// and its timestamps and `times_used` to the supplied values. Normally you
172    /// will use `update_credit_card` instead, which leaves `time_last_modified`
173    /// to this store; this is for keeping a record identical to one held
174    /// elsewhere. Errors with `NoSuchRecord` if the guid is absent.
175    #[handle_error(Error)]
176    pub fn update_credit_card_with_meta(
177        &self,
178        entry_with_meta: UpdatableCreditCardFieldsWithMeta,
179    ) -> ApiResult<()> {
180        credit_cards::update_credit_card_with_meta(
181            &self.lock_db()?.writer,
182            entry_with_meta.fields,
183            entry_with_meta.meta,
184        )
185    }
186
187    #[handle_error(Error)]
188    pub fn get_credit_card(&self, guid: String) -> ApiResult<CreditCard> {
189        let credit_card =
190            credit_cards::get_credit_card(&self.lock_db()?.writer, &Guid::new(&guid))?;
191        Ok(credit_card.into())
192    }
193
194    #[handle_error(Error)]
195    pub fn get_all_credit_cards(&self) -> ApiResult<Vec<CreditCard>> {
196        let credit_cards = credit_cards::get_all_credit_cards(&self.lock_db()?.writer)?
197            .into_iter()
198            .map(|x| x.into())
199            .collect();
200        Ok(credit_cards)
201    }
202
203    #[handle_error(Error)]
204    pub fn count_all_credit_cards(&self) -> ApiResult<i64> {
205        let count = credit_cards::count_all_credit_cards(&self.lock_db()?.writer)?;
206        Ok(count)
207    }
208
209    #[handle_error(Error)]
210    pub fn update_credit_card(
211        &self,
212        guid: String,
213        credit_card: UpdatableCreditCardFields,
214    ) -> ApiResult<()> {
215        credit_cards::update_credit_card(&self.lock_db()?.writer, &Guid::new(&guid), &credit_card)
216    }
217
218    #[handle_error(Error)]
219    pub fn delete_credit_card(&self, guid: String) -> ApiResult<bool> {
220        credit_cards::delete_credit_card(&self.lock_db()?.writer, &Guid::new(&guid))
221    }
222
223    #[handle_error(Error)]
224    pub fn touch_credit_card(&self, guid: String) -> ApiResult<()> {
225        credit_cards::touch(&self.lock_db()?.writer, &Guid::new(&guid))
226    }
227
228    #[handle_error(Error)]
229    pub fn add_address(&self, new_address: UpdatableAddressFields) -> ApiResult<Address> {
230        Ok(addresses::add_address(&self.lock_db()?.writer, new_address)?.into())
231    }
232
233    /// Adds an address **including metadata**. Normally you will use
234    /// `add_address` instead, and the metadata (guid, timestamps, change counter)
235    /// will be taken care of here. However, in some cases this method is
236    /// necessary, for example when migrating data from another store that
237    /// already contains the metadata.
238    #[handle_error(Error)]
239    pub fn add_address_with_meta(
240        &self,
241        entry_with_meta: UpdatableAddressFieldsWithMeta,
242    ) -> ApiResult<Address> {
243        Ok(addresses::add_address_with_meta(
244            &self.lock_db()?.writer,
245            entry_with_meta.fields,
246            entry_with_meta.meta,
247        )?
248        .into())
249    }
250
251    /// Adds multiple addresses **including metadata**, with a result per record.
252    #[handle_error(Error)]
253    pub fn add_many_addresses_with_meta(
254        &self,
255        entries_with_meta: Vec<UpdatableAddressFieldsWithMeta>,
256    ) -> ApiResult<Vec<AddressBulkResultEntry>> {
257        let results =
258            addresses::add_many_addresses_with_meta(&self.lock_db()?.writer, entries_with_meta)?;
259        Ok(results
260            .into_iter()
261            .map(|result| match result {
262                Ok(address) => AddressBulkResultEntry::Success {
263                    address: address.into(),
264                },
265                Err(message) => AddressBulkResultEntry::Error { message },
266            })
267            .collect())
268    }
269
270    /// Adds tombstones for addresses whose deletion has not yet been uploaded,
271    /// with a result per record.
272    #[handle_error(Error)]
273    pub fn add_many_address_tombstones(
274        &self,
275        tombstones: Vec<AddressTombstone>,
276    ) -> ApiResult<Vec<AddressBulkTombstoneResultEntry>> {
277        let results = addresses::add_many_address_tombstones(
278            &self.lock_db()?.writer,
279            tombstones
280                .into_iter()
281                .map(|t| (t.guid, t.time_deleted))
282                .collect(),
283        )?;
284        Ok(results
285            .into_iter()
286            .map(|result| match result {
287                Ok(guid) => AddressBulkTombstoneResultEntry::Success { guid },
288                Err(message) => AddressBulkTombstoneResultEntry::Error { message },
289            })
290            .collect())
291    }
292
293    /// Removes every address and every address tombstone.
294    ///
295    /// A migration primitive: it leaves the sync mirror intact and produces no
296    /// tombstones, so the deletions are never uploaded and a synced profile gets
297    /// the records back on the next sync. Use `delete_address` to delete on the
298    /// user's behalf.
299    #[handle_error(Error)]
300    pub fn delete_all_addresses(&self) -> ApiResult<()> {
301        addresses::delete_all_addresses(&self.lock_db()?.writer)?;
302        Ok(())
303    }
304
305    #[handle_error(Error)]
306    pub fn get_address(&self, guid: String) -> ApiResult<Address> {
307        Ok(addresses::get_address(&self.lock_db()?.writer, &Guid::new(&guid))?.into())
308    }
309
310    #[handle_error(Error)]
311    pub fn get_all_addresses(&self) -> ApiResult<Vec<Address>> {
312        let addresses = addresses::get_all_addresses(&self.lock_db()?.writer)?
313            .into_iter()
314            .map(|x| x.into())
315            .collect();
316        Ok(addresses)
317    }
318
319    #[handle_error(Error)]
320    pub fn count_all_addresses(&self) -> ApiResult<i64> {
321        let count = addresses::count_all_addresses(&self.lock_db()?.writer)?;
322        Ok(count)
323    }
324
325    #[handle_error(Error)]
326    pub fn update_address(&self, guid: String, address: UpdatableAddressFields) -> ApiResult<()> {
327        addresses::update_address(&self.lock_db()?.writer, &Guid::new(&guid), &address)
328    }
329
330    /// Updates an address **including metadata**, setting both its fields and
331    /// its timestamps and `times_used` to the supplied values. Normally you will
332    /// use `update_address` instead, which leaves `time_last_modified` to this
333    /// store; this is for keeping a record identical to one held elsewhere.
334    /// Errors with `NoSuchRecord` if the guid is absent.
335    #[handle_error(Error)]
336    pub fn update_address_with_meta(
337        &self,
338        entry_with_meta: UpdatableAddressFieldsWithMeta,
339    ) -> ApiResult<()> {
340        addresses::update_address_with_meta(
341            &self.lock_db()?.writer,
342            entry_with_meta.fields,
343            entry_with_meta.meta,
344        )
345    }
346
347    #[handle_error(Error)]
348    pub fn delete_address(&self, guid: String) -> ApiResult<bool> {
349        addresses::delete_address(&self.lock_db()?.writer, &Guid::new(&guid))
350    }
351
352    #[handle_error(Error)]
353    pub fn touch_address(&self, guid: String) -> ApiResult<()> {
354        addresses::touch(&self.lock_db()?.writer, &Guid::new(&guid))
355    }
356
357    #[handle_error(Error)]
358    pub fn add_passport(&self, fields: UpdatablePassportFields) -> ApiResult<Passport> {
359        Ok(passports::add_passport(&self.lock_db()?.writer, fields)?.into())
360    }
361
362    #[handle_error(Error)]
363    pub fn get_passport(&self, guid: String) -> ApiResult<Passport> {
364        Ok(passports::get_passport(&self.lock_db()?.writer, &Guid::new(&guid))?.into())
365    }
366
367    #[handle_error(Error)]
368    pub fn get_all_passports(&self) -> ApiResult<Vec<Passport>> {
369        let passports = passports::get_all_passports(&self.lock_db()?.writer)?
370            .into_iter()
371            .map(|x| x.into())
372            .collect();
373        Ok(passports)
374    }
375
376    #[handle_error(Error)]
377    pub fn count_all_passports(&self) -> ApiResult<i64> {
378        passports::count_all_passports(&self.lock_db()?.writer)
379    }
380
381    #[handle_error(Error)]
382    pub fn update_passport(
383        &self,
384        guid: String,
385        passport: UpdatablePassportFields,
386    ) -> ApiResult<()> {
387        passports::update_passport(&self.lock_db()?.writer, &Guid::new(&guid), &passport)
388    }
389
390    #[handle_error(Error)]
391    pub fn delete_passport(&self, guid: String) -> ApiResult<bool> {
392        passports::delete_passport(&self.lock_db()?.writer, &Guid::new(&guid))
393    }
394
395    #[handle_error(Error)]
396    pub fn touch_passport(&self, guid: String) -> ApiResult<()> {
397        passports::touch(&self.lock_db()?.writer, &Guid::new(&guid))
398    }
399
400    #[handle_error(Error)]
401    pub fn scrub_encrypted_data(self: Arc<Self>) -> ApiResult<()> {
402        // scrub the data on disk
403        // Currently only credit cards have encrypted data
404        credit_cards::scrub_encrypted_credit_card_data(&self.lock_db()?.writer)?;
405        // Force the sync engine to refetch data (only need to do this for the credit cards, since the
406        // addresses engine doesn't store encrypted data).
407        crate::sync::credit_card::create_engine(self).reset_local_sync_data()?;
408        Ok(())
409    }
410
411    #[handle_error(Error)]
412    pub fn scrub_undecryptable_credit_card_data_for_remote_replacement(
413        self: Arc<Self>,
414        local_encryption_key: String,
415    ) -> ApiResult<CreditCardsDeletionMetrics> {
416        let db = self.lock_db()?;
417        let deletion_stats =
418            credit_cards::scrub_undecryptable_credit_card_data_for_remote_replacement(
419                &db.writer,
420                local_encryption_key,
421            )?;
422
423        // Here we reset the local sync data so that the credit card engine syncs as if
424        // it were the first sync. This will potentially allow a previous sync of the
425        // record that exists on the sync server to overwrite the local record and restore
426        // the scrubbed credit card number.
427        crate::sync::credit_card::create_engine(self.clone())
428            .reset_local_sync_data_for_verification(&db.writer)?;
429        Ok(deletion_stats)
430    }
431
432    #[handle_error(Error)]
433    pub fn run_maintenance(&self) -> ApiResult<()> {
434        let conn = self.lock_db()?;
435        run_maintenance(&conn)?;
436        Ok(())
437    }
438
439    pub fn shutdown(&self) {
440        if let Some(db) = self.db.lock().take() {
441            db.close();
442        }
443    }
444
445    // This allows the embedding app to say "make this instance available to
446    // the sync manager". The implementation is more like "offer to sync mgr"
447    // (thereby avoiding us needing to link with the sync manager) but
448    // `register_with_sync_manager()` is logically what's happening so that's
449    // the name it gets.
450    pub fn register_with_sync_manager(self: Arc<Self>) {
451        let mut state = STORE_FOR_MANAGER.lock().unwrap();
452        *state = Arc::downgrade(&self);
453    }
454
455    // These 2 are a little odd - they aren't exposed by uniffi - currently the
456    // only consumer of this is our "example" (and hence why they
457    // are `pub` and not `pub(crate)`).
458    // We could probably make the example work with the sync manager - but then
459    // our example would link with places and logins etc, and it's not a big
460    // deal really.
461    pub fn create_credit_cards_sync_engine(self: Arc<Self>) -> Box<dyn SyncEngine> {
462        Box::new(crate::sync::credit_card::create_engine(self))
463    }
464
465    pub fn create_addresses_sync_engine(self: Arc<Self>) -> Box<dyn SyncEngine> {
466        Box::new(crate::sync::address::create_engine(self))
467    }
468}
469
470pub(crate) fn put_meta(conn: &Connection, key: &str, value: &dyn ToSql) -> Result<()> {
471    conn.execute_cached(
472        "REPLACE INTO moz_meta (key, value) VALUES (:key, :value)",
473        &[(":key", &key as &dyn ToSql), (":value", value)],
474    )?;
475    Ok(())
476}
477
478pub(crate) fn get_meta<T: FromSql>(conn: &Connection, key: &str) -> Result<Option<T>> {
479    let res = conn.try_query_one(
480        "SELECT value FROM moz_meta WHERE key = :key",
481        &[(":key", &key)],
482        true,
483    )?;
484    Ok(res)
485}
486
487pub(crate) fn delete_meta(conn: &Connection, key: &str) -> Result<()> {
488    conn.execute_cached("DELETE FROM moz_meta WHERE key = :key", &[(":key", &key)])?;
489    Ok(())
490}
491
492#[cfg(test)]
493mod tests {
494    use super::*;
495    use crate::db::test::new_mem_db;
496    use crate::encryption::EncryptorDecryptor;
497    use nss_as::ensure_initialized;
498
499    #[test]
500    fn test_autofill_meta() -> Result<()> {
501        let db = new_mem_db();
502        let test_key = "TEST KEY A";
503        let test_value = "TEST VALUE A";
504        let test_key2 = "TEST KEY B";
505        let test_value2 = "TEST VALUE B";
506
507        put_meta(&db, test_key, &test_value)?;
508        put_meta(&db, test_key2, &test_value2)?;
509
510        let retrieved_value: String = get_meta(&db, test_key)?.expect("test value");
511        let retrieved_value2: String = get_meta(&db, test_key2)?.expect("test value 2");
512
513        assert_eq!(retrieved_value, test_value);
514        assert_eq!(retrieved_value2, test_value2);
515
516        // check that the value of an existing key can be updated
517        let test_value3 = "TEST VALUE C";
518        put_meta(&db, test_key, &test_value3)?;
519
520        let retrieved_value3: String = get_meta(&db, test_key)?.expect("test value 3");
521
522        assert_eq!(retrieved_value3, test_value3);
523
524        // check that a deleted key is not retrieved
525        delete_meta(&db, test_key)?;
526        let retrieved_value4: Option<String> = get_meta(&db, test_key)?;
527        assert!(retrieved_value4.is_none());
528
529        db.writer.execute("DELETE FROM moz_meta", [])?;
530
531        Ok(())
532    }
533
534    #[test]
535    fn test_sync_manager_registration() {
536        let store = Arc::new(Store::new_shared_memory("sync-mgr-test").unwrap());
537        assert_eq!(Arc::strong_count(&store), 1);
538        assert_eq!(Arc::weak_count(&store), 0);
539        Arc::clone(&store).register_with_sync_manager();
540        assert_eq!(Arc::strong_count(&store), 1);
541        assert_eq!(Arc::weak_count(&store), 1);
542        let registered = STORE_FOR_MANAGER
543            .lock()
544            .unwrap()
545            .upgrade()
546            .expect("should upgrade");
547        assert!(Arc::ptr_eq(&store, &registered));
548        drop(registered);
549        // should be no new references
550        assert_eq!(Arc::strong_count(&store), 1);
551        assert_eq!(Arc::weak_count(&store), 1);
552        // dropping the registered object should drop the registration.
553        drop(store);
554        assert!(STORE_FOR_MANAGER.lock().unwrap().upgrade().is_none());
555    }
556
557    #[test]
558    fn test_shutdown_closes_the_store() {
559        let store = Store::new_shared_memory("shutdown-test").expect("create store");
560        // Operations succeed before shutdown.
561        assert_eq!(store.count_all_passports().expect("count"), 0);
562
563        store.shutdown();
564
565        // After shutdown, operations return an error rather than panicking or
566        // operating on a half-closed store.
567        assert!(store.count_all_passports().is_err());
568
569        // shutdown is idempotent.
570        store.shutdown();
571    }
572
573    #[test]
574    fn test_scrub_undecryptable_credit_card_data_for_remote_replacement() {
575        ensure_initialized();
576        let store = Arc::new(Store::new_shared_memory("sync-mgr-test").expect("create store"));
577        let key = EncryptorDecryptor::create_key().expect("create key");
578        let encdec = EncryptorDecryptor::new(&key).expect("create EncryptorDecryptor");
579
580        store
581            .add_credit_card(UpdatableCreditCardFields {
582                cc_name: "john deer".to_string(),
583                cc_number_enc: encdec
584                    .encrypt("567812345678123456781")
585                    .expect("encrypt cc number"),
586                cc_number_last_4: "6781".to_string(),
587                cc_exp_month: 10,
588                cc_exp_year: 2025,
589                cc_type: "mastercard".to_string(),
590            })
591            .expect("add credit card to database");
592
593        store
594            .scrub_undecryptable_credit_card_data_for_remote_replacement(key)
595            .expect("scrub credit card record");
596    }
597}