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