Merge pull request #5600 from element-hq/feature/bma/deletePinCode

Delete pin code only when the last session is deleted
This commit is contained in:
Benoit Marty 2025-10-24 09:47:57 +02:00 committed by GitHub
commit f1b8f878de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 181 additions and 67 deletions

View file

@ -35,9 +35,7 @@ class DefaultImageLoaderHolder(
private fun observeSessions() {
sessionObserver.addListener(object : SessionListener {
override suspend fun onSessionCreated(userId: String) = Unit
override suspend fun onSessionDeleted(userId: String) {
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
remove(SessionId(userId))
}
})

View file

@ -30,8 +30,7 @@ class DefaultSessionPreferencesStoreFactory(
init {
sessionObserver.addListener(object : SessionListener {
override suspend fun onSessionCreated(userId: String) = Unit
override suspend fun onSessionDeleted(userId: String) {
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
val sessionPreferences = cache.remove(SessionId(userId))
sessionPreferences?.clear()
}

View file

@ -108,10 +108,6 @@ class DefaultPushService(
sessionObserver.addListener(this)
}
override suspend fun onSessionCreated(userId: String) {
// Nothing to do
}
/**
* The session has been deleted.
* In this case, this is not necessary to unregister the pusher from the homeserver,
@ -119,7 +115,7 @@ class DefaultPushService(
* The current push provider may want to take action, and we need to
* cleanup the stores.
*/
override suspend fun onSessionDeleted(userId: String) {
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
val sessionId = SessionId(userId)
val userPushStore = userPushStoreFactory.getOrCreate(sessionId)
val currentPushProviderName = userPushStore.getPushProviderName()

View file

@ -59,9 +59,7 @@ class DefaultNotificationConversationService(
init {
sessionObserver.addListener(object : SessionListener {
override suspend fun onSessionCreated(userId: String) = Unit
override suspend fun onSessionDeleted(userId: String) {
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
onSessionLogOut(SessionId(userId))
}
})

View file

@ -248,7 +248,7 @@ class DefaultPushServiceTest {
),
pushClientSecretStore = pushClientSecretStore,
)
defaultPushService.onSessionDeleted(A_SESSION_ID.value)
defaultPushService.onSessionDeleted(A_SESSION_ID.value, false)
assertThat(userPushStore.getPushProviderName()).isNull()
assertThat(pushClientSecretStore.getSecret(A_SESSION_ID)).isNull()
onSessionDeletedLambda.assertions().isCalledOnce().with(value(A_SESSION_ID))
@ -268,7 +268,7 @@ class DefaultPushServiceTest {
),
pushClientSecretStore = pushClientSecretStore,
)
defaultPushService.onSessionDeleted(A_SESSION_ID.value)
defaultPushService.onSessionDeleted(A_SESSION_ID.value, false)
assertThat(userPushStore.getPushProviderName()).isNull()
assertThat(pushClientSecretStore.getSecret(A_SESSION_ID)).isNull()
}

View file

@ -8,6 +8,6 @@
package io.element.android.libraries.sessionstorage.api.observer
interface SessionListener {
suspend fun onSessionCreated(userId: String)
suspend fun onSessionDeleted(userId: String)
suspend fun onSessionCreated(userId: String) {}
suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {}
}

View file

@ -60,9 +60,10 @@ class DefaultSessionObserver(
// Compute diff
// Removed user
val removedUsers = currentUserSet - newUserSet
val wasLastSession = newUserSet.isEmpty()
removedUsers.forEach { removedUser ->
listeners.onEach { listener ->
listener.onSessionDeleted(removedUser)
listener.onSessionDeleted(removedUser, wasLastSession)
}
}
// Added user

View file

@ -24,7 +24,7 @@ class DatabaseSessionStoreTest {
private lateinit var database: SessionDatabase
private lateinit var databaseSessionStore: DatabaseSessionStore
private val aSessionData = aSessionData()
private val aSessionData = aDbSessionData()
@OptIn(ExperimentalCoroutinesApi::class)
@Before

View file

@ -10,8 +10,10 @@ package io.element.android.libraries.sessionstorage.impl
import io.element.android.libraries.matrix.session.SessionData
import io.element.android.libraries.sessionstorage.api.LoginType
internal fun aSessionData() = SessionData(
userId = "userId",
internal fun aDbSessionData(
userId: String = "userId",
) = SessionData(
userId = userId,
deviceId = "deviceId",
accessToken = "accessToken",
refreshToken = "refreshToken",

View file

@ -11,11 +11,10 @@ import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.sessionstorage.impl.DatabaseSessionStore
import io.element.android.libraries.sessionstorage.impl.SessionDatabase
import io.element.android.libraries.sessionstorage.impl.aSessionData
import io.element.android.libraries.sessionstorage.impl.aDbSessionData
import io.element.android.libraries.sessionstorage.impl.toApiModel
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
@ -23,7 +22,8 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class) class DefaultSessionObserverTest {
@OptIn(ExperimentalCoroutinesApi::class)
class DefaultSessionObserverTest {
private lateinit var database: SessionDatabase
private lateinit var databaseSessionStore: DatabaseSessionStore
@ -46,7 +46,7 @@ import org.junit.Test
@Test
fun `adding data invokes onSessionCreated`() = runTest {
val sessionData = aSessionData()
val sessionData = aDbSessionData()
val sut = createDefaultSessionObserver()
runCurrent()
val listener = TestSessionListener()
@ -54,12 +54,11 @@ import org.junit.Test
databaseSessionStore.addSession(sessionData.toApiModel())
listener.assertEvents(TestSessionListener.Event.Created(sessionData.userId))
sut.removeListener(listener)
coroutineContext.cancelChildren()
}
@Test
fun `adding and deleting data invokes onSessionCreated and onSessionDeleted`() = runTest {
val sessionData = aSessionData()
val sessionData = aDbSessionData()
val sut = createDefaultSessionObserver()
runCurrent()
val listener = TestSessionListener()
@ -69,15 +68,34 @@ import org.junit.Test
databaseSessionStore.removeSession(sessionData.userId)
listener.assertEvents(
TestSessionListener.Event.Created(sessionData.userId),
TestSessionListener.Event.Deleted(sessionData.userId),
TestSessionListener.Event.Deleted(sessionData.userId, true),
)
}
@Test
fun `adding and deleting data twice invokes onSessionCreated and onSessionDeleted`() = runTest {
val sessionData1 = aDbSessionData(userId = "user1")
val sessionData2 = aDbSessionData(userId = "user2")
val sut = createDefaultSessionObserver()
runCurrent()
val listener = TestSessionListener()
sut.addListener(listener)
databaseSessionStore.addSession(sessionData1.toApiModel())
databaseSessionStore.addSession(sessionData2.toApiModel())
databaseSessionStore.removeSession(sessionData2.userId)
databaseSessionStore.removeSession(sessionData1.userId)
listener.assertEvents(
TestSessionListener.Event.Created(sessionData1.userId),
TestSessionListener.Event.Created(sessionData2.userId),
TestSessionListener.Event.Deleted(sessionData2.userId, wasLastSession = false),
TestSessionListener.Event.Deleted(sessionData1.userId, wasLastSession = true),
)
coroutineContext.cancelChildren()
}
private fun TestScope.createDefaultSessionObserver(): DefaultSessionObserver {
return DefaultSessionObserver(
sessionStore = databaseSessionStore,
coroutineScope = this,
coroutineScope = backgroundScope,
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
)
}

View file

@ -13,7 +13,7 @@ import io.element.android.libraries.sessionstorage.api.observer.SessionListener
class TestSessionListener : SessionListener {
sealed interface Event {
data class Created(val userId: String) : Event
data class Deleted(val userId: String) : Event
data class Deleted(val userId: String, val wasLastSession: Boolean) : Event
}
private val trackRecord: MutableList<Event> = mutableListOf()
@ -22,8 +22,8 @@ class TestSessionListener : SessionListener {
trackRecord.add(Event.Created(userId))
}
override suspend fun onSessionDeleted(userId: String) {
trackRecord.add(Event.Deleted(userId))
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
trackRecord.add(Event.Deleted(userId, wasLastSession))
}
fun assertEvents(vararg events: Event) {

View file

@ -28,7 +28,7 @@ class FakeSessionObserver : SessionObserver {
listeners.forEach { it.onSessionCreated(userId) }
}
suspend fun onSessionDeleted(userId: String) {
listeners.forEach { it.onSessionDeleted(userId) }
suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean = true) {
listeners.forEach { it.onSessionDeleted(userId, wasLastSession = wasLastSession) }
}
}