From 67043177a91d54bb986619a1460e43b97fe58d26 Mon Sep 17 00:00:00 2001 From: link2xt Date: Mon, 11 Sep 2023 21:35:54 +0000 Subject: [PATCH] fix: reopen all connections on database passpharse change Previously only one connection, the one used to change the key, was working after passphrase change. With this fix the whole pool of connections is recreated on passphrase change, so there is no need to reopen the database manually. --- src/context.rs | 29 +++++++++++++++++++++++++++++ src/sql.rs | 36 +++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/context.rs b/src/context.rs index ba96035fb..78c0ed47f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1459,6 +1459,35 @@ mod tests { Ok(()) } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_context_change_passphrase() -> Result<()> { + let dir = tempdir()?; + let dbfile = dir.path().join("db.sqlite"); + + let id = 1; + let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new()) + .await + .context("failed to create context")?; + assert_eq!(context.open("foo".to_string()).await?, true); + assert_eq!(context.is_open().await, true); + + context + .set_config(Config::Addr, Some("alice@example.org")) + .await?; + + context + .change_passphrase("bar".to_string()) + .await + .context("Failed to change passphrase")?; + + assert_eq!( + context.get_config(Config::Addr).await?.unwrap(), + "alice@example.org" + ); + + Ok(()) + } + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_ongoing() -> Result<()> { let context = TestContext::new().await; diff --git a/src/sql.rs b/src/sql.rs index ab780a452..ceb38067e 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -310,12 +310,17 @@ impl Sql { /// It is impossible to turn encrypted database into unencrypted /// and vice versa this way, use import/export for this. pub async fn change_passphrase(&self, passphrase: String) -> Result<()> { - self.call_write(move |conn| { - conn.pragma_update(None, "rekey", passphrase) - .context("failed to set PRAGMA rekey")?; - Ok(()) - }) - .await + let mut lock = self.pool.write().await; + + let pool = lock.take().context("SQL connection pool is not open")?; + let conn = pool.get().await?; + conn.pragma_update(None, "rekey", passphrase.clone()) + .context("failed to set PRAGMA rekey")?; + drop(pool); + + *lock = Some(Self::new_pool(&self.dbfile, passphrase.to_string())?); + + Ok(()) } /// Locks the write transactions mutex in order to make sure that there never are @@ -1265,7 +1270,7 @@ mod tests { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn test_change_passphrase() -> Result<()> { + async fn test_sql_change_passphrase() -> Result<()> { use tempfile::tempdir; // The context is used only for logging. @@ -1289,6 +1294,23 @@ mod tests { sql.change_passphrase("bar".to_string()) .await .context("failed to change passphrase")?; + + // Test that at least two connections are still working. + // This ensures that not only the connection which changed the password is working, + // but other connections as well. + { + let lock = sql.pool.read().await; + let pool = lock.as_ref().unwrap(); + let conn1 = pool.get().await?; + let conn2 = pool.get().await?; + conn1 + .query_row("SELECT count(*) FROM sqlite_master", [], |_row| Ok(())) + .unwrap(); + conn2 + .query_row("SELECT count(*) FROM sqlite_master", [], |_row| Ok(())) + .unwrap(); + } + sql.close().await; let sql = Sql::new(dbfile);