Account deactivation.

This commit is contained in:
Benoit Marty 2024-09-17 13:19:46 +02:00
parent b94a5c9c51
commit b87bec6228
29 changed files with 1071 additions and 9 deletions

View file

@ -138,4 +138,7 @@ interface MatrixClient : Closeable {
/** Returns `true` if the current session is using native sliding sync, `false` if it's using a proxy. */
fun isUsingNativeSlidingSync(): Boolean
fun canDeactivateAccount(): Boolean
suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit>
}

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl
import io.element.android.libraries.androidutils.file.getSizeOfFiles
import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.matrix.api.MatrixClient
@ -89,6 +90,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.AuthData
import org.matrix.rustcomponents.sdk.AuthDataPasswordDetails
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientException
import org.matrix.rustcomponents.sdk.IgnoredUsersListener
@ -493,6 +496,46 @@ class RustMatrixClient(
return result
}
override fun canDeactivateAccount(): Boolean {
return runCatching {
client.canDeactivateAccount()
}
.getOrNull()
.orFalse()
}
override suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit> = withContext(sessionDispatcher) {
Timber.w("Deactivating account")
syncService.stop()
runCatching {
// First call without AuthData, should fail
val firstAttempt = runCatching {
client.deactivateAccount(
authData = null,
eraseData = eraseData,
)
}
if (firstAttempt.isFailure) {
Timber.w(firstAttempt.exceptionOrNull(), "Expected failure, try again")
// This is expected, try again with the password
client.deactivateAccount(
authData = AuthData.Password(
passwordDetails = AuthDataPasswordDetails(
identifier = sessionId.value,
password = password,
),
),
eraseData = eraseData,
)
}
close()
deleteSessionDirectory(deleteCryptoDb = true)
sessionStore.removeSession(sessionId.value)
}.onFailure {
Timber.e(it, "Failed to deactivate account")
}
}
override suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?> = withContext(sessionDispatcher) {
val rustAction = action?.toRustAction()
runCatching {

View file

@ -79,6 +79,8 @@ class FakeMatrixClient(
private val clearCacheLambda: () -> Unit = { lambdaError() },
private val userIdServerNameLambda: () -> String = { lambdaError() },
private val getUrlLambda: (String) -> Result<String> = { lambdaError() },
private val canDeactivateAccountResult: () -> Boolean = { lambdaError() },
private val deactivateAccountResult: (String, Boolean) -> Result<Unit> = { _, _ -> lambdaError() },
var isNativeSlidingSyncSupportedLambda: suspend () -> Boolean = { true },
var isSlidingSyncProxySupportedLambda: suspend () -> Boolean = { true },
var isUsingNativeSlidingSyncLambda: () -> Boolean = { true },
@ -175,6 +177,12 @@ class FakeMatrixClient(
return logoutLambda(ignoreSdkError, userInitiated)
}
override fun canDeactivateAccount() = canDeactivateAccountResult()
override suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit> = simulateLongTask {
deactivateAccountResult(password, eraseData)
}
override fun close() = Unit
override suspend fun getUserProfile(): Result<MatrixUser> = simulateLongTask {