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