indexeddb: migrate name and format of backup_version

The name was stupid, and this was the only string that was stored in the legacy
format; we can fix both problems with a cheeky migration.
This commit is contained in:
Richard van der Hoff
2024-07-03 19:41:49 +01:00
parent ab3ea8c467
commit bb0e50ce02
4 changed files with 80 additions and 7 deletions

View File

@@ -25,6 +25,7 @@ use crate::{
mod old_keys;
mod v0_to_v5;
mod v10_to_v11;
mod v5_to_v7;
mod v7;
mod v7_to_v8;
@@ -106,8 +107,12 @@ pub async fn open_and_upgrade_db(
v8_to_v10::schema_delete(name).await?;
}
if old_version < 11 {
v10_to_v11::data_migrate(name, serializer).await?;
}
// Open and return the DB (we know it's at the latest version)
Ok(IdbDatabase::open_u32(name, 10)?.await?)
Ok(IdbDatabase::open_u32(name, 11)?.await?)
}
async fn db_version(name: &str) -> Result<u32, IndexeddbCryptoStoreError> {
@@ -183,7 +188,10 @@ mod tests {
use super::{v0_to_v5, v7::InboundGroupSessionIndexedDbObject2};
use crate::{
crypto_store::{keys, migrations::*, InboundGroupSessionIndexedDbObject},
crypto_store::{
indexeddb_serializer::MaybeEncrypted, keys, migrations::*,
InboundGroupSessionIndexedDbObject,
},
IndexeddbCryptoStore,
};

View File

@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(doc)]
use crate::crypto_store::keys::BACKUP_VERSION_V1;
/// Old format of the `inbound_group_sessions` store which lacked indexes or
/// a sensible structure
pub const INBOUND_GROUP_SESSIONS_V1: &str = "inbound_group_sessions";
@@ -20,3 +23,6 @@ pub const INBOUND_GROUP_SESSIONS_V1: &str = "inbound_group_sessions";
/// JSON-encoding and arrays of ints instead of base64.
/// Also lacked the `backed_up_to` property+index.
pub const INBOUND_GROUP_SESSIONS_V2: &str = "inbound_group_sessions2";
/// An old name for [`BACKUP_VERSION_V1`].
pub const BACKUP_KEY_V1: &str = "backup_key_v1";

View File

@@ -0,0 +1,54 @@
// Copyright 2024 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Migration code that moves from `backup_keys.backup_key_v1` to
//! `backup_keys.backup_version_v1`, switching to a new serialization format.
use indexed_db_futures::IdbQuerySource;
use wasm_bindgen::JsValue;
use web_sys::IdbTransactionMode;
use crate::crypto_store::{
indexeddb_serializer::IndexeddbSerializer,
keys,
migrations::{old_keys, MigrationDb},
};
/// Migrate data from `backup_keys.backup_key_v1` to
/// `backup_keys.backup_version_v1`.
pub(crate) async fn data_migrate(
name: &str,
serializer: &IndexeddbSerializer,
) -> crate::crypto_store::Result<()> {
let db = MigrationDb::new(name, 11).await?;
let txn = db.transaction_on_one_with_mode(keys::BACKUP_KEYS, IdbTransactionMode::Readwrite)?;
let store = txn.object_store(keys::BACKUP_KEYS)?;
let bv = store.get(&JsValue::from_str(old_keys::BACKUP_KEY_V1))?.await?;
let Some(bv) = bv else {
return Ok(());
};
// backup_key_v1 was only ever serialized with the legacy format. Also, it's a
// string, so if we use `deserialize_value` on it, it will be incorrectly
// handled as a new-format object.
let bv: String = serializer.deserialize_legacy_value(bv)?;
// Re-serialize as new format, then store in the new field.
let serialized = serializer.serialize_value(&bv)?;
store.put_key_val(&JsValue::from_str(keys::BACKUP_VERSION_V1), &serialized)?.await?;
store.delete(&JsValue::from_str(old_keys::BACKUP_KEY_V1))?.await?;
Ok(())
}

View File

@@ -90,7 +90,11 @@ mod keys {
// backup v1
pub const BACKUP_KEYS: &str = "backup_keys";
pub const BACKUP_KEY_V1: &str = "backup_key_v1";
/// Indexeddb key for the key backup version that [`RECOVERY_KEY_V1`]
/// corresponds to.
pub const BACKUP_VERSION_V1: &str = "backup_version_v1";
/// Indexeddb key for the backup decryption key.
///
/// Known, for historical reasons, as the recovery key. Not to be confused
@@ -479,9 +483,10 @@ impl IndexeddbCryptoStore {
}
if let Some(a) = &backup_version {
indexeddb_changes
.get(keys::BACKUP_KEYS)
.put(JsValue::from_str(keys::BACKUP_KEY_V1), self.serializer.serialize_value(&a)?);
indexeddb_changes.get(keys::BACKUP_KEYS).put(
JsValue::from_str(keys::BACKUP_VERSION_V1),
self.serializer.serialize_value(&a)?,
);
}
if !changes.sessions.is_empty() {
@@ -1208,7 +1213,7 @@ impl_crypto_store! {
let store = tx.object_store(keys::BACKUP_KEYS)?;
let backup_version = store
.get(&JsValue::from_str(keys::BACKUP_KEY_V1))?
.get(&JsValue::from_str(keys::BACKUP_VERSION_V1))?
.await?
.map(|i| self.serializer.deserialize_value(i))
.transpose()?;