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