Browse Source

Merge pull request #1961 from vector-im/feature/bma/keyBackupIteration

Key backup iteration
pull/1963/head
Benoit Marty 10 months ago committed by GitHub
parent
commit
274bc89e11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt
  2. 4
      features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt
  3. 4
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt
  4. 25
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt
  5. 37
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt
  6. 15
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt
  7. 10
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt

2
features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenter.kt

@ -89,7 +89,7 @@ class SecureBackupEnterRecoveryKeyPresenter @Inject constructor( @@ -89,7 +89,7 @@ class SecureBackupEnterRecoveryKeyPresenter @Inject constructor(
action: MutableState<Async<Unit>>
) = launch {
suspend {
encryptionService.fixRecoveryIssues(recoveryKey).getOrThrow()
encryptionService.recover(recoveryKey).getOrThrow()
}.runCatchingUpdatingState(action)
}
}

4
features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyPresenterTest.kt

@ -73,7 +73,7 @@ class SecureBackupEnterRecoveryKeyPresenterTest { @@ -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 { @@ -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>())

4
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt

@ -43,9 +43,9 @@ interface EncryptionService { @@ -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.

25
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/RecoveryException.kt

@ -0,0 +1,25 @@ @@ -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")
}

37
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RecoveryExceptionMapper.kt

@ -0,0 +1,37 @@ @@ -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")
)
}
}

15
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt

@ -17,6 +17,7 @@ @@ -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( @@ -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( @@ -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( @@ -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()
}
}
}

10
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt

@ -33,7 +33,7 @@ class FakeEncryptionService : EncryptionService { @@ -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 { @@ -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 { @@ -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)
}

Loading…
Cancel
Save