When logging out from Pin code screen, logout from all the sessions.
This commit is contained in:
parent
a2d728d29e
commit
be61b89bfb
6 changed files with 146 additions and 20 deletions
|
|
@ -174,7 +174,7 @@ class PinUnlockPresenter(
|
||||||
|
|
||||||
private fun CoroutineScope.signOut(signOutAction: MutableState<AsyncAction<Unit>>) = launch {
|
private fun CoroutineScope.signOut(signOutAction: MutableState<AsyncAction<Unit>>) = launch {
|
||||||
suspend {
|
suspend {
|
||||||
logoutUseCase.logout(ignoreSdkError = true)
|
logoutUseCase.logoutAll(ignoreSdkError = true)
|
||||||
}.runCatchingUpdatingState(signOutAction)
|
}.runCatchingUpdatingState(signOutAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,12 @@
|
||||||
package io.element.android.features.logout.api
|
package io.element.android.features.logout.api
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to trigger a log out of the current user from any part of the app.
|
* Used to trigger a log out of the current user(s) from any part of the app.
|
||||||
*/
|
*/
|
||||||
interface LogoutUseCase {
|
interface LogoutUseCase {
|
||||||
/**
|
/**
|
||||||
* Log out the current user and then perform any needed cleanup tasks.
|
* Log out the current user(s) and then perform any needed cleanup tasks.
|
||||||
* @param ignoreSdkError if true, the SDK error will be ignored and the user will be logged out anyway.
|
* @param ignoreSdkError if true, the SDK error will be ignored and the user will be logged out anyway.
|
||||||
*/
|
*/
|
||||||
suspend fun logout(ignoreSdkError: Boolean)
|
suspend fun logoutAll(ignoreSdkError: Boolean)
|
||||||
|
|
||||||
interface Factory {
|
|
||||||
fun create(sessionId: String): LogoutUseCase
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,5 @@ dependencies {
|
||||||
testCommonDependencies(libs, true)
|
testCommonDependencies(libs, true)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.featureflag.test)
|
testImplementation(projects.libraries.featureflag.test)
|
||||||
|
testImplementation(projects.libraries.sessionStorage.test)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,22 +12,31 @@ import dev.zacsweers.metro.ContributesBinding
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.features.logout.api.LogoutUseCase
|
import io.element.android.features.logout.api.LogoutUseCase
|
||||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
@Inject
|
@Inject
|
||||||
class DefaultLogoutUseCase(
|
class DefaultLogoutUseCase(
|
||||||
private val authenticationService: MatrixAuthenticationService,
|
private val sessionStore: SessionStore,
|
||||||
private val matrixClientProvider: MatrixClientProvider,
|
private val matrixClientProvider: MatrixClientProvider,
|
||||||
) : LogoutUseCase {
|
) : LogoutUseCase {
|
||||||
override suspend fun logout(ignoreSdkError: Boolean) {
|
override suspend fun logoutAll(ignoreSdkError: Boolean) {
|
||||||
val currentSession = authenticationService.getLatestSessionId()
|
sessionStore.getAllSessions()
|
||||||
if (currentSession != null) {
|
.map { sessionData ->
|
||||||
matrixClientProvider.getOrRestore(currentSession)
|
SessionId(sessionData.userId)
|
||||||
.getOrThrow()
|
}
|
||||||
.logout(userInitiated = true, ignoreSdkError = true)
|
.forEach { sessionId ->
|
||||||
} else {
|
Timber.d("Logging out sessionId: $sessionId")
|
||||||
error("No session to sign out")
|
matrixClientProvider.getOrRestore(sessionId).fold(
|
||||||
}
|
onSuccess = { client ->
|
||||||
|
client.logout(userInitiated = true, ignoreSdkError = ignoreSdkError)
|
||||||
|
},
|
||||||
|
onFailure = { error ->
|
||||||
|
Timber.e(error, "Failed to get or restore MatrixClient for sessionId: $sessionId")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
|
package io.element.android.features.logout.impl
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||||
|
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||||
|
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
|
||||||
|
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||||
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
|
import io.element.android.tests.testutils.lambda.value
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class DefaultLogoutUseCaseTest {
|
||||||
|
@Test
|
||||||
|
fun `test logout from one session`() = runTest {
|
||||||
|
val logoutLambda1 = lambdaRecorder<Boolean, Boolean, Unit> { _, _ -> }
|
||||||
|
val client1 = FakeMatrixClient(A_USER_ID).apply {
|
||||||
|
logoutLambda = logoutLambda1
|
||||||
|
}
|
||||||
|
val sut = DefaultLogoutUseCase(
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
initialList = listOf(
|
||||||
|
aSessionData(sessionId = A_USER_ID.value),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
matrixClientProvider = FakeMatrixClientProvider(
|
||||||
|
getClient = { sessionId ->
|
||||||
|
when (sessionId) {
|
||||||
|
A_USER_ID -> Result.success(client1)
|
||||||
|
else -> error("Unexpected sessionId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sut.logoutAll(ignoreSdkError = true)
|
||||||
|
logoutLambda1.assertions().isCalledOnce().with(value(true), value(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test logout from several sessions`() = runTest {
|
||||||
|
val logoutLambda1 = lambdaRecorder<Boolean, Boolean, Unit> { _, _ -> }
|
||||||
|
val logoutLambda2 = lambdaRecorder<Boolean, Boolean, Unit> { _, _ -> }
|
||||||
|
val client1 = FakeMatrixClient(A_USER_ID).apply {
|
||||||
|
logoutLambda = logoutLambda1
|
||||||
|
}
|
||||||
|
val client2 = FakeMatrixClient(A_USER_ID_2).apply {
|
||||||
|
logoutLambda = logoutLambda2
|
||||||
|
}
|
||||||
|
val sut = DefaultLogoutUseCase(
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
initialList = listOf(
|
||||||
|
aSessionData(sessionId = A_USER_ID.value),
|
||||||
|
aSessionData(sessionId = A_USER_ID_2.value),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
matrixClientProvider = FakeMatrixClientProvider(
|
||||||
|
getClient = { sessionId ->
|
||||||
|
when (sessionId) {
|
||||||
|
A_USER_ID -> Result.success(client1)
|
||||||
|
A_USER_ID_2 -> Result.success(client2)
|
||||||
|
else -> error("Unexpected sessionId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sut.logoutAll(ignoreSdkError = true)
|
||||||
|
logoutLambda1.assertions().isCalledOnce().with(value(true), value(true))
|
||||||
|
logoutLambda2.assertions().isCalledOnce().with(value(true), value(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test logout session not found is ignored`() = runTest {
|
||||||
|
val sut = DefaultLogoutUseCase(
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
initialList = listOf(
|
||||||
|
aSessionData(sessionId = A_USER_ID.value),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
matrixClientProvider = FakeMatrixClientProvider(
|
||||||
|
getClient = { sessionId ->
|
||||||
|
when (sessionId) {
|
||||||
|
A_USER_ID -> Result.failure(Exception("Session not found"))
|
||||||
|
else -> error("Unexpected sessionId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sut.logoutAll(ignoreSdkError = true)
|
||||||
|
// No error
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test logout no sessions`() = runTest {
|
||||||
|
val sut = DefaultLogoutUseCase(
|
||||||
|
sessionStore = InMemorySessionStore(
|
||||||
|
initialList = emptyList()
|
||||||
|
),
|
||||||
|
matrixClientProvider = FakeMatrixClientProvider(
|
||||||
|
getClient = { sessionId ->
|
||||||
|
when (sessionId) {
|
||||||
|
else -> error("Unexpected sessionId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sut.logoutAll(ignoreSdkError = true)
|
||||||
|
// No error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ import io.element.android.tests.testutils.simulateLongTask
|
||||||
class FakeLogoutUseCase(
|
class FakeLogoutUseCase(
|
||||||
var logoutLambda: (Boolean) -> Unit = { lambdaError() }
|
var logoutLambda: (Boolean) -> Unit = { lambdaError() }
|
||||||
) : LogoutUseCase {
|
) : LogoutUseCase {
|
||||||
override suspend fun logout(ignoreSdkError: Boolean) = simulateLongTask {
|
override suspend fun logoutAll(ignoreSdkError: Boolean) = simulateLongTask {
|
||||||
logoutLambda(ignoreSdkError)
|
logoutLambda(ignoreSdkError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue