Skip to content

Commit 1c6375a

Browse files
fix(keystore/wasm): stop using Database::open() in keystore migrations
This would result in infinite recursion
1 parent 098aacf commit 1c6375a

File tree

4 files changed

+94
-123
lines changed

4 files changed

+94
-123
lines changed

keystore/src/connection/platform/wasm/migrations/v6/mod.rs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,36 @@
11
//! This migration merges signature keypair and credential data
2-
32
mod v5_entities;
43

54
use idb::builder::DatabaseBuilder;
65

76
use super::DB_VERSION_6;
87
use crate::{
98
CryptoKeystoreResult, Database, DatabaseKey,
10-
connection::FetchFromDatabase,
11-
entities::{EntityBase as _, EntityFindParams, EntityTransactionExt, StoredCredential},
9+
entities::{Entity, EntityBase as _, EntityFindParams, EntityTransactionExt},
1210
migrations::{StoredSignatureKeypair, V5Credential, migrate_to_new_credential},
1311
};
1412

1513
/// Open IDB once with the new builder and close it, this will apply the update.
1614
pub(super) async fn migrate(name: &str, key: &DatabaseKey) -> CryptoKeystoreResult<u32> {
17-
let db_before_migration = Database::open(crate::ConnectionType::Persistent(name), key).await?;
18-
let signature_keys = db_before_migration
19-
.find_all::<StoredSignatureKeypair>(EntityFindParams::default())
20-
.await?;
21-
let v5_credentials = db_before_migration
22-
.find_all::<V5Credential>(EntityFindParams::default())
23-
.await?;
24-
25-
let connection = db_before_migration.conn().await?;
26-
let mut tx_creator = connection.conn().await;
27-
let tx = tx_creator
28-
.new_transaction(&[&StoredCredential::COLLECTION_NAME])
29-
.await?;
30-
31-
for signature_key in signature_keys.iter() {
32-
for v5_credential in v5_credentials.iter() {
33-
if let Some(new_credential) = migrate_to_new_credential(v5_credential, signature_key)? {
34-
super::delete_credential_by_value(&tx, v5_credential.credential.clone()).await?;
35-
new_credential.save(&tx).await?;
15+
let previous_builder = super::v5::get_builder(name);
16+
let mut db_during_migration = Database::migration_connection(previous_builder, key).await?;
17+
let signature_keys =
18+
StoredSignatureKeypair::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
19+
let v5_credentials = V5Credential::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
20+
21+
Database::migration_transaction(db_during_migration, async |tx| {
22+
for signature_key in signature_keys.iter() {
23+
for v5_credential in v5_credentials.iter() {
24+
if let Some(new_credential) = migrate_to_new_credential(v5_credential, signature_key)? {
25+
super::delete_credential_by_value(tx, v5_credential.credential.clone()).await?;
26+
new_credential.save(tx).await?;
27+
}
3628
}
3729
}
38-
}
3930

40-
db_before_migration.commit_transaction().await?;
41-
db_before_migration.close().await?;
31+
Ok(())
32+
})
33+
.await?;
4234

4335
let migrated_idb = get_builder(name).build().await?;
4436
let version = migrated_idb.version()?;

keystore/src/connection/platform/wasm/migrations/v7/mod.rs

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,37 @@ use idb::builder::DatabaseBuilder;
55
use super::DB_VERSION_7;
66
use crate::{
77
CryptoKeystoreResult, Database, DatabaseKey,
8-
connection::FetchFromDatabase as _,
9-
entities::{EntityBase as _, EntityFindParams, EntityTransactionExt as _, PersistedMlsGroup, StoredCredential},
8+
entities::{Entity as _, EntityFindParams, EntityTransactionExt as _, PersistedMlsGroup, StoredCredential},
109
migrations::{V6Credential, make_ciphersuite_for_signature_scheme},
1110
};
1211

1312
/// Open IDB once with the new builder and close it, this will apply the update.
1413
pub(super) async fn migrate(name: &str, key: &DatabaseKey) -> CryptoKeystoreResult<u32> {
15-
let db_before_migration = Database::open(crate::ConnectionType::Persistent(name), key).await?;
16-
let persisted_mls_groups = db_before_migration
17-
.find_all::<PersistedMlsGroup>(EntityFindParams::default())
18-
.await?;
14+
let previous_builder = super::v6::get_builder(name);
15+
let mut db_during_migration = Database::migration_connection(previous_builder, key).await?;
16+
let persisted_mls_groups =
17+
PersistedMlsGroup::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
1918
let ciphersuite_for_signature_scheme = make_ciphersuite_for_signature_scheme(persisted_mls_groups)?;
20-
let v6_credentials = db_before_migration
21-
.find_all::<V6Credential>(EntityFindParams::default())
22-
.await?;
23-
24-
let connection = db_before_migration.conn().await?;
25-
let mut tx_creator = connection.conn().await;
26-
let tx = tx_creator
27-
.new_transaction(&[&StoredCredential::COLLECTION_NAME])
28-
.await?;
29-
30-
for v6_credential in v6_credentials {
31-
if let Some(ciphersuite) = ciphersuite_for_signature_scheme(v6_credential.signature_scheme) {
32-
let new_credential = StoredCredential {
33-
ciphersuite,
34-
id: v6_credential.id.clone(),
35-
credential: v6_credential.credential.clone(),
36-
created_at: v6_credential.created_at,
37-
public_key: v6_credential.public_key.clone(),
38-
secret_key: v6_credential.secret_key.clone(),
39-
};
40-
new_credential.save(&tx).await?;
41-
super::delete_credential_by_value(&tx, v6_credential.credential.clone()).await?;
19+
let v6_credentials = V6Credential::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
20+
21+
Database::migration_transaction(db_during_migration, async |tx| {
22+
for v6_credential in v6_credentials {
23+
if let Some(ciphersuite) = ciphersuite_for_signature_scheme(v6_credential.signature_scheme) {
24+
let new_credential = StoredCredential {
25+
ciphersuite,
26+
id: v6_credential.id.clone(),
27+
credential: v6_credential.credential.clone(),
28+
created_at: v6_credential.created_at,
29+
public_key: v6_credential.public_key.clone(),
30+
secret_key: v6_credential.secret_key.clone(),
31+
};
32+
new_credential.save(tx).await?;
33+
super::delete_credential_by_value(tx, v6_credential.credential.clone()).await?;
34+
}
4235
}
43-
}
44-
45-
db_before_migration.commit_transaction().await?;
46-
db_before_migration.close().await?;
36+
Ok(())
37+
})
38+
.await?;
4739

4840
let migrated_idb = get_builder(name).build().await?;
4941
let version = migrated_idb.version()?;

keystore/src/connection/platform/wasm/migrations/v8.rs

Lines changed: 36 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,50 @@ use idb::{
88
use super::DB_VERSION_8;
99
use crate::{
1010
CryptoKeystoreResult, Database, DatabaseKey,
11-
connection::FetchFromDatabase as _,
12-
entities::{EntityBase, EntityFindParams, PersistedMlsGroup, StoredCredential},
11+
entities::{Entity as _, EntityBase, EntityFindParams, PersistedMlsGroup, StoredCredential},
1312
migrations::{detect_duplicate_credentials, make_least_used_ciphersuite},
1413
};
1514

1615
/// Open IDB once with the new builder and close it, this will apply the update.
1716
pub(super) async fn migrate(name: &str, key: &DatabaseKey) -> CryptoKeystoreResult<u32> {
18-
let db_before_migration = Database::open(crate::ConnectionType::Persistent(name), key).await?;
19-
let persisted_mls_groups = db_before_migration
20-
.find_all::<PersistedMlsGroup>(EntityFindParams::default())
21-
.await?;
17+
let previous_builder = super::v7::get_builder(name);
18+
let mut db_during_migration = Database::migration_connection(previous_builder, key).await?;
19+
let persisted_mls_groups =
20+
PersistedMlsGroup::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
21+
2222
let least_used_ciphersuite = make_least_used_ciphersuite(persisted_mls_groups)?;
23-
let credentials = db_before_migration
24-
.find_all::<StoredCredential>(EntityFindParams::default())
25-
.await?;
23+
let credentials = StoredCredential::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
2624
let duplicates = detect_duplicate_credentials(&credentials);
2725

28-
let connection = db_before_migration.conn().await?;
29-
let mut tx_creator = connection.conn().await;
30-
let tx = tx_creator
31-
.new_transaction(&[&StoredCredential::COLLECTION_NAME])
32-
.await?;
33-
34-
for (cred_a, cred_b) in duplicates.into_iter() {
35-
let least_used_ciphersuite = least_used_ciphersuite(cred_a.ciphersuite, cred_b.ciphersuite);
36-
match least_used_ciphersuite {
37-
None => {
38-
// If the least used ciphersuite couldn't be determined, something in the data is not what we assume
39-
//
40-
// a) the duplicate doesn't form a pair of ciphersuites with a matching signature scheme (error in
41-
// previous meta migration)
42-
//
43-
// b) both ciphersuites don't get used in any mls group
44-
//
45-
// In both cases, what we want to do is delete both credentials.
46-
super::delete_credential_by_value(&tx, cred_a.credential.clone()).await?;
47-
super::delete_credential_by_value(&tx, cred_b.credential.clone()).await?;
48-
}
49-
Some(least_used_ciphersuite) => {
50-
let cred_to_delete = if least_used_ciphersuite == cred_a.ciphersuite {
51-
cred_a.credential.clone()
52-
} else {
53-
cred_b.credential.clone()
54-
};
55-
super::delete_credential_by_value(&tx, cred_to_delete).await?;
56-
}
57-
};
58-
}
59-
60-
db_before_migration.commit_transaction().await?;
61-
db_before_migration.close().await?;
26+
Database::migration_transaction(db_during_migration, async |tx| {
27+
for (cred_a, cred_b) in duplicates.into_iter() {
28+
let least_used_ciphersuite = least_used_ciphersuite(cred_a.ciphersuite, cred_b.ciphersuite);
29+
match least_used_ciphersuite {
30+
None => {
31+
// If the least used ciphersuite couldn't be determined, something in the data is not what we assume
32+
//
33+
// a) the duplicate doesn't form a pair of ciphersuites with a matching signature scheme (error in
34+
// previous meta migration)
35+
//
36+
// b) both ciphersuites don't get used in any mls group
37+
//
38+
// In both cases, what we want to do is delete both credentials.
39+
super::delete_credential_by_value(tx, cred_a.credential.clone()).await?;
40+
super::delete_credential_by_value(tx, cred_b.credential.clone()).await?;
41+
}
42+
Some(least_used_ciphersuite) => {
43+
let cred_to_delete = if least_used_ciphersuite == cred_a.ciphersuite {
44+
cred_a.credential.clone()
45+
} else {
46+
cred_b.credential.clone()
47+
};
48+
super::delete_credential_by_value(tx, cred_to_delete).await?;
49+
}
50+
};
51+
}
52+
Ok(())
53+
})
54+
.await?;
6255

6356
let migrated_idb = get_builder(name).build().await?;
6457
let version = migrated_idb.version()?;

keystore/src/connection/platform/wasm/migrations/v9.rs

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,39 @@ use super::DB_VERSION_9;
88
use crate::{
99
CryptoKeystoreResult, Database, DatabaseKey,
1010
connection::platform::wasm::WasmStorageTransaction,
11-
entities::{Entity as _, EntityBase, StoredCredential},
11+
entities::{Entity as _, EntityBase, EntityFindParams, StoredCredential},
1212
};
1313

1414
/// Open IDB once with the new builder and close it, this will apply the update.
1515
pub(super) async fn migrate(name: &str, key: &DatabaseKey) -> CryptoKeystoreResult<u32> {
16-
let db_before_migration = Database::open(crate::ConnectionType::Persistent(name), key).await?;
17-
let connection = db_before_migration.conn().await?;
18-
let credentials = connection
19-
.storage()
20-
.get_all::<StoredCredential>(StoredCredential::COLLECTION_NAME, None)
21-
.await?;
16+
let previous_builder = super::v8::get_builder(name);
17+
let mut db_during_migration = Database::migration_connection(previous_builder, key).await?;
18+
let credentials = StoredCredential::find_all(&mut db_during_migration, EntityFindParams::default()).await?;
2219

2320
let collection_name = format!(
2421
"{collection_name}_new",
2522
collection_name = StoredCredential::COLLECTION_NAME
2623
);
2724

28-
db_before_migration.new_transaction().await?;
29-
let mut tx_creator = connection.conn().await;
30-
let mut tx = tx_creator.new_transaction(&[&collection_name]).await?;
31-
32-
for mut credential in credentials {
33-
let serializer = serde_wasm_bindgen::Serializer::json_compatible();
34-
let key = &js_sys::Uint8Array::from(credential.public_key.as_slice()).into();
25+
Database::migration_transaction(db_during_migration, async |tx| {
3526
match tx {
36-
WasmStorageTransaction::Persistent { ref mut tx, cipher } => {
37-
credential.encrypt(cipher)?;
38-
let js_value = credential.serialize(&serializer)?;
27+
WasmStorageTransaction::Persistent { tx, cipher } => {
28+
let serializer = serde_wasm_bindgen::Serializer::json_compatible();
3929
let store = tx.object_store(&collection_name)?;
40-
store.put(&js_value, Some(key))?.await?;
30+
for mut credential in credentials {
31+
let key = &js_sys::Uint8Array::from(credential.public_key.as_slice()).into();
32+
credential.encrypt(cipher)?;
33+
let js_value = credential.serialize(&serializer)?;
34+
store.put(&js_value, Some(key))?.await?;
35+
}
4136
}
4237
WasmStorageTransaction::InMemory { .. } => {
4338
// in memory transaction is never used in production.
4439
}
4540
}
46-
}
47-
48-
tx.commit_tx().await?;
49-
db_before_migration.close().await?;
41+
Ok(())
42+
})
43+
.await?;
5044

5145
let migrated_idb = get_builder(name).build().await?;
5246
let version = migrated_idb.version()?;
@@ -57,7 +51,7 @@ pub(super) async fn migrate(name: &str, key: &DatabaseKey) -> CryptoKeystoreResu
5751
/// Set up the builder for v9.
5852
pub(super) fn get_builder(name: &str) -> DatabaseBuilder {
5953
let collection_name = StoredCredential::COLLECTION_NAME;
60-
let collection_name_with_prefix = &format!("{collection_name}-new",);
54+
let collection_name_with_prefix = &format!("{collection_name}_new",);
6155

6256
super::v8::get_builder(name)
6357
.version(DB_VERSION_9)

0 commit comments

Comments
 (0)