Add tests for extracting and migrate data
This commit is contained in:
parent
f9f885418a
commit
88733784cd
|
@ -83,6 +83,11 @@ android {
|
||||||
test {
|
test {
|
||||||
java.srcDirs += "src/sharedTest/java"
|
java.srcDirs += "src/sharedTest/java"
|
||||||
}
|
}
|
||||||
|
main {
|
||||||
|
assets {
|
||||||
|
srcDirs 'src/main/assets', 'src/androidTest/assets'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import io.realm.Realm
|
||||||
|
import org.amshove.kluent.internal.assertFails
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
import org.matrix.android.sdk.common.TemporaryRealmConfigurationFactory
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.migration.fixtures.rustCryptoStoreMigrationConfiguration
|
||||||
|
import org.matrix.olm.OlmManager
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExtractMigrationDataUseCaseTest : InstrumentedTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val realmConfigurationFactory = TemporaryRealmConfigurationFactory()
|
||||||
|
|
||||||
|
private val extractMigrationData = ExtractMigrationDataUseCase()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
// Ensure Olm is initialized
|
||||||
|
OlmManager()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun given_a_valid_crypto_store_realm_file_then_extraction_should_be_successful() {
|
||||||
|
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = true)
|
||||||
|
val migrationData = Realm.getInstance(realmConfiguration).use {
|
||||||
|
extractMigrationData(it)
|
||||||
|
}
|
||||||
|
assertNotNull(migrationData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun given_an_empty_crypto_store_realm_file_then_extraction_should_throw() {
|
||||||
|
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = false)
|
||||||
|
assertFails {
|
||||||
|
Realm.getInstance(realmConfiguration).use {
|
||||||
|
extractMigrationData(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.amshove.kluent.internal.assertEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
import org.matrix.android.sdk.common.TemporaryRealmConfigurationFactory
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.migration.fixtures.rustCryptoStoreMigrationConfiguration
|
||||||
|
import org.matrix.olm.OlmManager
|
||||||
|
import uniffi.olm.OlmMachine
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class RustCryptoStoreMigrateUseCaseTest : InstrumentedTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val realmConfigurationFactory = TemporaryRealmConfigurationFactory()
|
||||||
|
|
||||||
|
private val extractMigrationData = ExtractMigrationDataUseCase()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
// Ensure Olm is initialized
|
||||||
|
OlmManager()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() = runBlocking {
|
||||||
|
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = true)
|
||||||
|
val cryptoStoreMigrate = RustCryptoStoreMigrateUseCase(realmConfiguration, realmConfigurationFactory.root, extractMigrationData)
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
val progressListener = ProgressListener(latch)
|
||||||
|
val result = cryptoStoreMigrate(progressListener)
|
||||||
|
latch.await()
|
||||||
|
assert(result.isSuccess)
|
||||||
|
|
||||||
|
val machine = OlmMachine("@ganfra146:matrix.org", "UTDQCHKKNS",realmConfigurationFactory.root.path, null)
|
||||||
|
assertEquals("mW7LWO4zmhH8Ttuvmzn27vm/USXSKBPgmg7FKQITLiU", machine.identityKeys()["ed25519"])
|
||||||
|
assertNotNull(machine.getBackupKeys())
|
||||||
|
val crossSigningStatus = machine.crossSigningStatus()
|
||||||
|
assertTrue(crossSigningStatus.hasMaster)
|
||||||
|
assertTrue(crossSigningStatus.hasSelfSigning)
|
||||||
|
assertTrue(crossSigningStatus.hasUserSigning)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun given_an_empty_crypto_store_realm_file_then_migration_should_fail() = runBlocking {
|
||||||
|
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = false)
|
||||||
|
val cryptoStoreMigrate = RustCryptoStoreMigrateUseCase(realmConfiguration, realmConfigurationFactory.root, extractMigrationData)
|
||||||
|
val progressListener = ProgressListener()
|
||||||
|
val result = cryptoStoreMigrate(progressListener)
|
||||||
|
assert(result.isFailure)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProgressListener(val latch: CountDownLatch? = null) : uniffi.olm.ProgressListener {
|
||||||
|
override fun onProgress(progress: Int, total: Int) {
|
||||||
|
if (progress == total) {
|
||||||
|
latch?.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.crypto.store.migration.fixtures
|
||||||
|
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import org.matrix.android.sdk.common.TemporaryRealmConfigurationFactory
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||||
|
|
||||||
|
fun TemporaryRealmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore: Boolean): RealmConfiguration {
|
||||||
|
return create(
|
||||||
|
realmFilename = "crypto_store_rust_migration.realm",
|
||||||
|
assetFilename = "crypto_store_rust_migration.realm".takeIf { populateCryptoStore },
|
||||||
|
schemaVersion = 15L,
|
||||||
|
module = RealmCryptoStoreModule()
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||||
|
|
||||||
|
class CleanUpCryptoStoreUseCase {
|
||||||
|
}
|
|
@ -35,7 +35,15 @@ private val charset = Charset.forName("UTF-8")
|
||||||
|
|
||||||
internal class ExtractMigrationDataUseCase @Inject constructor() {
|
internal class ExtractMigrationDataUseCase @Inject constructor() {
|
||||||
|
|
||||||
operator fun invoke(realm: Realm): MigrationData? {
|
operator fun invoke(realm: Realm): MigrationData {
|
||||||
|
return try {
|
||||||
|
extract(realm) ?: throw ExtractMigrationDataFailure
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
throw ExtractMigrationDataFailure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extract(realm: Realm): MigrationData? {
|
||||||
val metadataEntity = realm.where<CryptoMetadataEntity>().findFirst() ?: return null
|
val metadataEntity = realm.where<CryptoMetadataEntity>().findFirst() ?: return null
|
||||||
|
|
||||||
val pickleKey = OlmUtility.getRandomKey()
|
val pickleKey = OlmUtility.getRandomKey()
|
||||||
|
@ -61,7 +69,7 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
||||||
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
|
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
|
||||||
|
|
||||||
val olmAccount = metadataEntity.getOlmAccount()!!
|
val olmAccount = metadataEntity.getOlmAccount()!!
|
||||||
val pickledOlmAccount = olmAccount.pickle(pickleKey)
|
val pickledOlmAccount = olmAccount.pickle(pickleKey, StringBuffer()).asString()
|
||||||
val pickledAccount = PickledAccount(
|
val pickledAccount = PickledAccount(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
deviceId = deviceId,
|
deviceId = deviceId,
|
||||||
|
@ -88,7 +96,7 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
||||||
private fun OlmInboundGroupSessionEntity.toPickledInboundGroupSession(pickleKey: ByteArray): PickledInboundGroupSession {
|
private fun OlmInboundGroupSessionEntity.toPickledInboundGroupSession(pickleKey: ByteArray): PickledInboundGroupSession {
|
||||||
val senderKey = this.senderKey ?: ""
|
val senderKey = this.senderKey ?: ""
|
||||||
val olmInboundGroupSession = getInboundGroupSession()!!
|
val olmInboundGroupSession = getInboundGroupSession()!!
|
||||||
val pickledInboundGroupSession = olmInboundGroupSession.olmInboundGroupSession!!.pickle(pickleKey)
|
val pickledInboundGroupSession = olmInboundGroupSession.olmInboundGroupSession!!.pickle(pickleKey, StringBuffer()).asString()
|
||||||
return PickledInboundGroupSession(
|
return PickledInboundGroupSession(
|
||||||
pickle = pickledInboundGroupSession,
|
pickle = pickledInboundGroupSession,
|
||||||
senderKey = senderKey,
|
senderKey = senderKey,
|
||||||
|
@ -104,7 +112,7 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
||||||
val deviceKey = this.deviceKey ?: ""
|
val deviceKey = this.deviceKey ?: ""
|
||||||
val lastReceivedMessageTs = this.lastReceivedMessageTs
|
val lastReceivedMessageTs = this.lastReceivedMessageTs
|
||||||
val olmSession = getOlmSession()!!
|
val olmSession = getOlmSession()!!
|
||||||
val pickledOlmSession = olmSession.pickle(pickleKey)
|
val pickledOlmSession = olmSession.pickle(pickleKey, StringBuffer()).asString()
|
||||||
return PickledSession(
|
return PickledSession(
|
||||||
pickle = pickledOlmSession,
|
pickle = pickledOlmSession,
|
||||||
senderKey = deviceKey,
|
senderKey = deviceKey,
|
||||||
|
@ -114,14 +122,5 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Any.pickle(pickleKey: ByteArray): String {
|
private fun ByteArray.asString() = String(this, charset)
|
||||||
return try {
|
|
||||||
val pickleMethod = this.javaClass.getDeclaredMethod("serialize", ByteArray::class.java, StringBuffer::class.java)
|
|
||||||
pickleMethod.isAccessible = true
|
|
||||||
val pickled = pickleMethod.invoke(this, pickleKey, StringBuffer())!!
|
|
||||||
String(pickled as ByteArray, charset)
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||||
|
|
||||||
|
object ExtractMigrationDataFailure : java.lang.RuntimeException("Can't proceed with migration, crypto store is empty or some necessary data is missing.")
|
|
@ -21,7 +21,6 @@ import io.realm.RealmConfiguration
|
||||||
import org.matrix.android.sdk.internal.database.awaitTransaction
|
import org.matrix.android.sdk.internal.database.awaitTransaction
|
||||||
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||||
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
|
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
|
||||||
import timber.log.Timber
|
|
||||||
import uniffi.olm.ProgressListener
|
import uniffi.olm.ProgressListener
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -38,10 +37,6 @@ internal class RustCryptoStoreMigrateUseCase @Inject constructor(
|
||||||
private suspend fun migrate(progressListener: ProgressListener) {
|
private suspend fun migrate(progressListener: ProgressListener) {
|
||||||
awaitTransaction(realmConfiguration) { realm: Realm ->
|
awaitTransaction(realmConfiguration) { realm: Realm ->
|
||||||
val migrationData = extractMigrationData(realm)
|
val migrationData = extractMigrationData(realm)
|
||||||
if (migrationData == null) {
|
|
||||||
Timber.v("No migration to do, return")
|
|
||||||
return@awaitTransaction
|
|
||||||
}
|
|
||||||
uniffi.olm.migrate(migrationData, dataDir.path, null, progressListener)
|
uniffi.olm.migrate(migrationData, dataDir.path, null, progressListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue