mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
refactor(sql): recreate keypairs table
Removed unused `addr` and `created` field. `is_default` boolean flag is moved into `config` row pointing to the current default key.
This commit is contained in:
@@ -18,7 +18,7 @@ use crate::log::LogExt;
|
|||||||
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
||||||
use crate::provider::{get_provider_by_id, Provider};
|
use crate::provider::{get_provider_by_id, Provider};
|
||||||
use crate::sync::{self, Sync::*, SyncData};
|
use crate::sync::{self, Sync::*, SyncData};
|
||||||
use crate::tools::{get_abs_path, improve_single_line_input, EmailAddress};
|
use crate::tools::{get_abs_path, improve_single_line_input};
|
||||||
|
|
||||||
/// The available configuration keys.
|
/// The available configuration keys.
|
||||||
#[derive(
|
#[derive(
|
||||||
@@ -343,6 +343,10 @@ pub enum Config {
|
|||||||
/// until `chat_id.accept()` is called.
|
/// until `chat_id.accept()` is called.
|
||||||
#[strum(props(default = "0"))]
|
#[strum(props(default = "0"))]
|
||||||
VerifiedOneOnOneChats,
|
VerifiedOneOnOneChats,
|
||||||
|
|
||||||
|
/// Row ID of the key in the `keypairs` table
|
||||||
|
/// used for signatures, encryption to self and included in `Autocrypt` header.
|
||||||
|
KeyId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -627,8 +631,6 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// This should only be used by test code and during configure.
|
/// This should only be used by test code and during configure.
|
||||||
pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> {
|
pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> {
|
||||||
let old_addr = self.get_config(Config::ConfiguredAddr).await?;
|
|
||||||
|
|
||||||
// add old primary address (if exists) to secondary addresses
|
// add old primary address (if exists) to secondary addresses
|
||||||
let mut secondary_addrs = self.get_all_self_addrs().await?;
|
let mut secondary_addrs = self.get_all_self_addrs().await?;
|
||||||
// never store a primary address also as a secondary
|
// never store a primary address also as a secondary
|
||||||
@@ -642,17 +644,6 @@ impl Context {
|
|||||||
self.set_config(Config::ConfiguredAddr, Some(primary_new))
|
self.set_config(Config::ConfiguredAddr, Some(primary_new))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(old_addr) = old_addr {
|
|
||||||
let old_addr = EmailAddress::new(&old_addr)?;
|
|
||||||
let old_keypair = crate::key::load_keypair(self, &old_addr).await?;
|
|
||||||
|
|
||||||
if let Some(mut old_keypair) = old_keypair {
|
|
||||||
old_keypair.addr = EmailAddress::new(primary_new)?;
|
|
||||||
crate::key::store_self_keypair(self, &old_keypair, crate::key::KeyPairUse::Default)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1318,6 +1318,7 @@ mod tests {
|
|||||||
"socks5_port",
|
"socks5_port",
|
||||||
"socks5_user",
|
"socks5_user",
|
||||||
"socks5_password",
|
"socks5_password",
|
||||||
|
"key_id",
|
||||||
];
|
];
|
||||||
let t = TestContext::new().await;
|
let t = TestContext::new().await;
|
||||||
let info = t.get_info().await.unwrap();
|
let info = t.get_info().await.unwrap();
|
||||||
|
|||||||
@@ -670,7 +670,7 @@ async fn export_self_keys(context: &Context, dir: &Path) -> Result<()> {
|
|||||||
let keys = context
|
let keys = context
|
||||||
.sql
|
.sql
|
||||||
.query_map(
|
.query_map(
|
||||||
"SELECT id, public_key, private_key, is_default FROM keypairs;",
|
"SELECT id, public_key, private_key, id=(SELECT value FROM config WHERE keyname='key_id') FROM keypairs;",
|
||||||
(),
|
(),
|
||||||
|row| {
|
|row| {
|
||||||
let id = row.get(0)?;
|
let id = row.get(0)?;
|
||||||
|
|||||||
76
src/key.rs
76
src/key.rs
@@ -18,7 +18,7 @@ use crate::constants::KeyGenType;
|
|||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::log::LogExt;
|
use crate::log::LogExt;
|
||||||
use crate::pgp::KeyPair;
|
use crate::pgp::KeyPair;
|
||||||
use crate::tools::{time, EmailAddress};
|
use crate::tools::EmailAddress;
|
||||||
|
|
||||||
/// Convenience trait for working with keys.
|
/// Convenience trait for working with keys.
|
||||||
///
|
///
|
||||||
@@ -82,10 +82,9 @@ pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPubl
|
|||||||
match context
|
match context
|
||||||
.sql
|
.sql
|
||||||
.query_row_optional(
|
.query_row_optional(
|
||||||
r#"SELECT public_key
|
"SELECT public_key
|
||||||
FROM keypairs
|
FROM keypairs
|
||||||
WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr")
|
WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
|
||||||
AND is_default=1"#,
|
|
||||||
(),
|
(),
|
||||||
|row| {
|
|row| {
|
||||||
let bytes: Vec<u8> = row.get(0)?;
|
let bytes: Vec<u8> = row.get(0)?;
|
||||||
@@ -106,10 +105,9 @@ pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecr
|
|||||||
match context
|
match context
|
||||||
.sql
|
.sql
|
||||||
.query_row_optional(
|
.query_row_optional(
|
||||||
r#"SELECT private_key
|
"SELECT private_key
|
||||||
FROM keypairs
|
FROM keypairs
|
||||||
WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr")
|
WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
|
||||||
AND is_default=1"#,
|
|
||||||
(),
|
(),
|
||||||
|row| {
|
|row| {
|
||||||
let bytes: Vec<u8> = row.get(0)?;
|
let bytes: Vec<u8> = row.get(0)?;
|
||||||
@@ -132,8 +130,7 @@ pub(crate) async fn load_self_secret_keyring(context: &Context) -> Result<Vec<Si
|
|||||||
.query_map(
|
.query_map(
|
||||||
r#"SELECT private_key
|
r#"SELECT private_key
|
||||||
FROM keypairs
|
FROM keypairs
|
||||||
WHERE addr=(SELECT value FROM config WHERE keyname="configured_addr")
|
ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
|
||||||
ORDER BY is_default DESC"#,
|
|
||||||
(),
|
(),
|
||||||
|row| row.get::<_, Vec<u8>>(0),
|
|row| row.get::<_, Vec<u8>>(0),
|
||||||
|keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
|
|keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
|
||||||
@@ -233,13 +230,10 @@ pub(crate) async fn load_keypair(
|
|||||||
let res = context
|
let res = context
|
||||||
.sql
|
.sql
|
||||||
.query_row_optional(
|
.query_row_optional(
|
||||||
r#"
|
"SELECT public_key, private_key
|
||||||
SELECT public_key, private_key
|
FROM keypairs
|
||||||
FROM keypairs
|
WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
|
||||||
WHERE addr=?1
|
(),
|
||||||
AND is_default=1;
|
|
||||||
"#,
|
|
||||||
(addr,),
|
|
||||||
|row| {
|
|row| {
|
||||||
let pub_bytes: Vec<u8> = row.get(0)?;
|
let pub_bytes: Vec<u8> = row.get(0)?;
|
||||||
let sec_bytes: Vec<u8> = row.get(1)?;
|
let sec_bytes: Vec<u8> = row.get(1)?;
|
||||||
@@ -288,42 +282,44 @@ pub async fn store_self_keypair(
|
|||||||
keypair: &KeyPair,
|
keypair: &KeyPair,
|
||||||
default: KeyPairUse,
|
default: KeyPairUse,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
context
|
let mut config_cache_lock = context.sql.config_cache.write().await;
|
||||||
|
let new_key_id = context
|
||||||
.sql
|
.sql
|
||||||
.transaction(|transaction| {
|
.transaction(|transaction| {
|
||||||
let public_key = DcKey::to_bytes(&keypair.public);
|
let public_key = DcKey::to_bytes(&keypair.public);
|
||||||
let secret_key = DcKey::to_bytes(&keypair.secret);
|
let secret_key = DcKey::to_bytes(&keypair.secret);
|
||||||
transaction
|
|
||||||
.execute(
|
|
||||||
"DELETE FROM keypairs WHERE public_key=? OR private_key=?;",
|
|
||||||
(&public_key, &secret_key),
|
|
||||||
)
|
|
||||||
.context("failed to remove old use of key")?;
|
|
||||||
if default == KeyPairUse::Default {
|
|
||||||
transaction
|
|
||||||
.execute("UPDATE keypairs SET is_default=0;", ())
|
|
||||||
.context("failed to clear default")?;
|
|
||||||
}
|
|
||||||
let is_default = match default {
|
let is_default = match default {
|
||||||
KeyPairUse::Default => i32::from(true),
|
KeyPairUse::Default => true,
|
||||||
KeyPairUse::ReadOnly => i32::from(false),
|
KeyPairUse::ReadOnly => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let addr = keypair.addr.to_string();
|
|
||||||
let t = time();
|
|
||||||
|
|
||||||
transaction
|
transaction
|
||||||
.execute(
|
.execute(
|
||||||
"INSERT INTO keypairs (addr, is_default, public_key, private_key, created)
|
"INSERT OR REPLACE INTO keypairs (public_key, private_key)
|
||||||
VALUES (?,?,?,?,?);",
|
VALUES (?,?)",
|
||||||
(addr, is_default, &public_key, &secret_key, t),
|
(&public_key, &secret_key),
|
||||||
)
|
)
|
||||||
.context("failed to insert keypair")?;
|
.context("Failed to insert keypair")?;
|
||||||
|
|
||||||
Ok(())
|
if is_default {
|
||||||
|
let new_key_id = transaction.last_insert_rowid();
|
||||||
|
transaction.execute(
|
||||||
|
"INSERT OR REPLACE INTO config (keyname, value) VALUES ('key_id', ?)",
|
||||||
|
(new_key_id,),
|
||||||
|
)?;
|
||||||
|
Ok(Some(new_key_id))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if let Some(new_key_id) = new_key_id {
|
||||||
|
// Update config cache if transaction succeeded and changed current default key.
|
||||||
|
config_cache_lock.insert("key_id".to_string(), Some(new_key_id.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -804,6 +804,36 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dbversion < 107 {
|
||||||
|
sql.execute_migration(
|
||||||
|
"CREATE TABLE new_keypairs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
private_key UNIQUE NOT NULL,
|
||||||
|
public_key UNIQUE NOT NULL
|
||||||
|
);
|
||||||
|
INSERT OR IGNORE INTO new_keypairs SELECT id, private_key, public_key FROM keypairs;
|
||||||
|
|
||||||
|
INSERT OR IGNORE
|
||||||
|
INTO config (keyname, value)
|
||||||
|
VALUES
|
||||||
|
('key_id', (SELECT id FROM new_keypairs
|
||||||
|
WHERE private_key=
|
||||||
|
(SELECT private_key FROM keypairs
|
||||||
|
WHERE addr=(SELECT value FROM config WHERE keyname='configured_addr')
|
||||||
|
AND is_default=1)));
|
||||||
|
|
||||||
|
-- We do not drop the old `keypairs` table for now,
|
||||||
|
-- but move it to `old_keypairs`. We can remove it later
|
||||||
|
-- in next migrations. This may be needed for recovery
|
||||||
|
-- in case something is wrong with the migration.
|
||||||
|
ALTER TABLE keypairs RENAME TO old_keypairs;
|
||||||
|
ALTER TABLE new_keypairs RENAME TO keypairs;
|
||||||
|
",
|
||||||
|
107,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
let new_version = sql
|
let new_version = sql
|
||||||
.get_raw_config_int(VERSION_CFG)
|
.get_raw_config_int(VERSION_CFG)
|
||||||
.await?
|
.await?
|
||||||
|
|||||||
Reference in New Issue
Block a user