Merge pull request #1961 from vector-im/feature/bma/keyBackupIteration
Key backup iteration
This commit is contained in:
@@ -89,7 +89,7 @@ class SecureBackupEnterRecoveryKeyPresenter @Inject constructor(
|
||||
action: MutableState<Async<Unit>>
|
||||
) = launch {
|
||||
suspend {
|
||||
encryptionService.fixRecoveryIssues(recoveryKey).getOrThrow()
|
||||
encryptionService.recover(recoveryKey).getOrThrow()
|
||||
}.runCatchingUpdatingState(action)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest {
|
||||
inProgress = false,
|
||||
)
|
||||
)
|
||||
encryptionService.givenFixRecoveryIssuesFailure(AN_EXCEPTION)
|
||||
encryptionService.givenRecoverFailure(AN_EXCEPTION)
|
||||
withRecoveryKeyState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit)
|
||||
val loadingState = awaitItem()
|
||||
assertThat(loadingState.submitAction).isEqualTo(Async.Loading<Unit>())
|
||||
@@ -85,7 +85,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest {
|
||||
val clearedState = awaitItem()
|
||||
assertThat(clearedState.submitAction).isEqualTo(Async.Uninitialized)
|
||||
assertThat(clearedState.isSubmitEnabled).isTrue()
|
||||
encryptionService.givenFixRecoveryIssuesFailure(null)
|
||||
encryptionService.givenRecoverFailure(null)
|
||||
clearedState.eventSink(SecureBackupEnterRecoveryKeyEvents.Submit)
|
||||
val loadingState2 = awaitItem()
|
||||
assertThat(loadingState2.submitAction).isEqualTo(Async.Loading<Unit>())
|
||||
|
||||
@@ -43,9 +43,9 @@ interface EncryptionService {
|
||||
suspend fun doesBackupExistOnServer(): Result<Boolean>
|
||||
|
||||
/**
|
||||
* Note: accept bot recoveryKey and passphrase.
|
||||
* Note: accept both recoveryKey and passphrase.
|
||||
*/
|
||||
suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit>
|
||||
suspend fun recover(recoveryKey: String): Result<Unit>
|
||||
|
||||
/**
|
||||
* Wait for backup upload steady state.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 io.element.android.libraries.matrix.api.encryption
|
||||
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
|
||||
sealed class RecoveryException(message: String) : Exception(message) {
|
||||
class SecretStorage(message: String) : RecoveryException(message)
|
||||
data object BackupExistsOnServer : RecoveryException("BackupExistsOnServer")
|
||||
data class Client(val exception: ClientException) : RecoveryException(exception.message ?: "Unknown error")
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.encryption
|
||||
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryException
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||
import org.matrix.rustcomponents.sdk.RecoveryException as RustRecoveryException
|
||||
|
||||
fun Throwable.mapRecoveryException(): RecoveryException {
|
||||
return when (this) {
|
||||
is RustRecoveryException.SecretStorage -> RecoveryException.SecretStorage(
|
||||
message = errorMessage
|
||||
)
|
||||
is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer
|
||||
is RustRecoveryException.Client -> RecoveryException.Client(
|
||||
source.mapClientException()
|
||||
)
|
||||
else -> RecoveryException.Client(
|
||||
ClientException.Other("Unknown error")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.element.android.libraries.matrix.impl.encryption
|
||||
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress
|
||||
@@ -110,6 +111,8 @@ internal class RustEncryptionService(
|
||||
override suspend fun enableBackups(): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
service.enableBackups()
|
||||
}.mapFailure {
|
||||
it.mapRecoveryException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +130,8 @@ internal class RustEncryptionService(
|
||||
)
|
||||
// enableRecovery returns the encryption key, but we read it from the state flow
|
||||
.let { }
|
||||
}.mapFailure {
|
||||
it.mapRecoveryException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,24 +169,32 @@ internal class RustEncryptionService(
|
||||
override suspend fun disableRecovery(): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
service.disableRecovery()
|
||||
}.mapFailure {
|
||||
it.mapRecoveryException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun isLastDevice(): Result<Boolean> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
service.isLastDevice()
|
||||
}.mapFailure {
|
||||
it.mapRecoveryException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetRecoveryKey(): Result<String> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
service.resetRecoveryKey()
|
||||
}.mapFailure {
|
||||
it.mapRecoveryException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
|
||||
override suspend fun recover(recoveryKey: String): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
service.recover(recoveryKey)
|
||||
}.mapFailure {
|
||||
it.mapRecoveryException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class FakeEncryptionService : EncryptionService {
|
||||
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
|
||||
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
|
||||
|
||||
private var fixRecoveryIssuesFailure: Exception? = null
|
||||
private var recoverFailure: Exception? = null
|
||||
private var doesBackupExistOnServerResult: Result<Boolean> = Result.success(true)
|
||||
|
||||
override suspend fun enableBackups(): Result<Unit> = simulateLongTask {
|
||||
@@ -44,8 +44,8 @@ class FakeEncryptionService : EncryptionService {
|
||||
disableRecoveryFailure = exception
|
||||
}
|
||||
|
||||
fun givenFixRecoveryIssuesFailure(exception: Exception?) {
|
||||
fixRecoveryIssuesFailure = exception
|
||||
fun givenRecoverFailure(exception: Exception?) {
|
||||
recoverFailure = exception
|
||||
}
|
||||
|
||||
override suspend fun disableRecovery(): Result<Unit> = simulateLongTask {
|
||||
@@ -61,8 +61,8 @@ class FakeEncryptionService : EncryptionService {
|
||||
return doesBackupExistOnServerResult
|
||||
}
|
||||
|
||||
override suspend fun fixRecoveryIssues(recoveryKey: String): Result<Unit> = simulateLongTask {
|
||||
fixRecoveryIssuesFailure?.let { return Result.failure(it) }
|
||||
override suspend fun recover(recoveryKey: String): Result<Unit> = simulateLongTask {
|
||||
recoverFailure?.let { return Result.failure(it) }
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user