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:
commit
f1b8f878de
19 changed files with 181 additions and 67 deletions
|
|
@ -33,8 +33,7 @@ class DefaultSeenInvitesStore(
|
|||
) : SeenInvitesStore {
|
||||
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) {
|
||||
if (sessionId.value == userId) {
|
||||
clear()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,12 +35,6 @@ interface LockScreenService {
|
|||
fun isPinSetup(): Flow<Boolean>
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the app is currently locked.
|
||||
*/
|
||||
val LockScreenService.isLocked: Boolean
|
||||
get() = lockState.value == LockScreenLockState.Locked
|
||||
|
||||
/**
|
||||
* Makes sure the secure flag is set on the activity if the pin is setup.
|
||||
* @param activity the activity to set the flag on.
|
||||
|
|
|
|||
|
|
@ -73,15 +73,14 @@ class DefaultLockScreenService(
|
|||
}
|
||||
|
||||
/**
|
||||
* Makes sure to delete the pin code when the session is deleted.
|
||||
* Makes sure to delete the pin code when the last session is deleted.
|
||||
*/
|
||||
private fun observeSessionsState() {
|
||||
sessionObserver.addListener(object : SessionListener {
|
||||
override suspend fun onSessionCreated(userId: String) = Unit
|
||||
|
||||
override suspend fun onSessionDeleted(userId: String) {
|
||||
// TODO handle multi session at some point
|
||||
pinCodeManager.deletePinCode()
|
||||
override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {
|
||||
if (wasLastSession) {
|
||||
pinCodeManager.deletePinCode()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package io.element.android.features.lockscreen.impl
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager
|
||||
import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthenticatorManager
|
||||
import io.element.android.features.lockscreen.impl.fixtures.aLockScreenConfig
|
||||
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||
import io.element.android.features.lockscreen.impl.pin.createDefaultPinCodeManager
|
||||
import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore
|
||||
import io.element.android.features.lockscreen.impl.storage.LockScreenStore
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver
|
||||
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
||||
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultLockScreenServiceTest {
|
||||
@Test
|
||||
fun `when the pin is not mandatory and no pin is configured isSetupRequired emits false`() = runTest {
|
||||
val sut = createDefaultLockScreenService(
|
||||
lockScreenConfig = aLockScreenConfig(isPinMandatory = false)
|
||||
)
|
||||
sut.isSetupRequired().test {
|
||||
assertThat(awaitItem()).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when the pin is mandatory, isSetupRequired emits true`() = runTest {
|
||||
val lockScreenStore = InMemoryLockScreenStore()
|
||||
val sut = createDefaultLockScreenService(
|
||||
lockScreenConfig = aLockScreenConfig(isPinMandatory = true),
|
||||
lockScreenStore = lockScreenStore,
|
||||
)
|
||||
sut.isSetupRequired().test {
|
||||
assertThat(awaitItem()).isTrue()
|
||||
// When the user configures the pin code, the setup is not required anymore
|
||||
lockScreenStore.saveEncryptedPinCode("encryptedCode")
|
||||
assertThat(awaitItem()).isFalse()
|
||||
// Users deletes the pin code
|
||||
lockScreenStore.deleteEncryptedPinCode()
|
||||
assertThat(awaitItem()).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when the last session is deleted, the pin code is removed`() = runTest {
|
||||
val sessionObserver = FakeSessionObserver()
|
||||
val lockScreenStore = InMemoryLockScreenStore()
|
||||
val sut = createDefaultLockScreenService(
|
||||
lockScreenConfig = aLockScreenConfig(isPinMandatory = true),
|
||||
lockScreenStore = lockScreenStore,
|
||||
sessionObserver = sessionObserver,
|
||||
)
|
||||
sut.isPinSetup().test {
|
||||
assertThat(awaitItem()).isFalse()
|
||||
// When the user configure the pin code, the setup is not required anymore
|
||||
lockScreenStore.saveEncryptedPinCode("encryptedCode")
|
||||
assertThat(awaitItem()).isTrue()
|
||||
sessionObserver.onSessionDeleted("userId", wasLastSession = false)
|
||||
expectNoEvents()
|
||||
sessionObserver.onSessionDeleted("userId", wasLastSession = true)
|
||||
assertThat(awaitItem()).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createDefaultLockScreenService(
|
||||
lockScreenConfig: LockScreenConfig = aLockScreenConfig(),
|
||||
lockScreenStore: LockScreenStore = InMemoryLockScreenStore(),
|
||||
pinCodeManager: PinCodeManager = createDefaultPinCodeManager(
|
||||
lockScreenStore = lockScreenStore,
|
||||
),
|
||||
sessionObserver: SessionObserver = FakeSessionObserver(),
|
||||
appForegroundStateService: AppForegroundStateService = FakeAppForegroundStateService(),
|
||||
biometricAuthenticatorManager: BiometricAuthenticatorManager = FakeBiometricAuthenticatorManager(),
|
||||
) = DefaultLockScreenService(
|
||||
lockScreenConfig = lockScreenConfig,
|
||||
lockScreenStore = lockScreenStore,
|
||||
pinCodeManager = pinCodeManager,
|
||||
coroutineScope = backgroundScope,
|
||||
sessionObserver = sessionObserver,
|
||||
appForegroundStateService = appForegroundStateService,
|
||||
biometricAuthenticatorManager = biometricAuthenticatorManager,
|
||||
)
|
||||
|
|
@ -10,19 +10,18 @@ package io.element.android.features.lockscreen.impl.pin
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore
|
||||
import io.element.android.features.lockscreen.impl.storage.LockScreenStore
|
||||
import io.element.android.libraries.cryptography.api.EncryptionDecryptionService
|
||||
import io.element.android.libraries.cryptography.api.SecretKeyRepository
|
||||
import io.element.android.libraries.cryptography.impl.AESEncryptionDecryptionService
|
||||
import io.element.android.libraries.cryptography.test.SimpleSecretKeyRepository
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultPinCodeManagerTest {
|
||||
private val lockScreenStore = InMemoryLockScreenStore()
|
||||
private val secretKeyRepository = SimpleSecretKeyRepository()
|
||||
private val encryptionDecryptionService = AESEncryptionDecryptionService()
|
||||
private val pinCodeManager = DefaultPinCodeManager(secretKeyRepository, encryptionDecryptionService, lockScreenStore)
|
||||
|
||||
@Test
|
||||
fun `given a pin code when create and delete assert no pin code left`() = runTest {
|
||||
val pinCodeManager = createDefaultPinCodeManager()
|
||||
pinCodeManager.hasPinCode().test {
|
||||
assertThat(awaitItem()).isFalse()
|
||||
pinCodeManager.createPinCode("1234")
|
||||
|
|
@ -34,6 +33,7 @@ class DefaultPinCodeManagerTest {
|
|||
|
||||
@Test
|
||||
fun `given a pin code when create and verify with the same pin succeed`() = runTest {
|
||||
val pinCodeManager = createDefaultPinCodeManager()
|
||||
val pinCode = "1234"
|
||||
pinCodeManager.createPinCode(pinCode)
|
||||
assertThat(pinCodeManager.verifyPinCode(pinCode)).isTrue()
|
||||
|
|
@ -41,7 +41,18 @@ class DefaultPinCodeManagerTest {
|
|||
|
||||
@Test
|
||||
fun `given a pin code when create and verify with a different pin fails`() = runTest {
|
||||
val pinCodeManager = createDefaultPinCodeManager()
|
||||
pinCodeManager.createPinCode("1234")
|
||||
assertThat(pinCodeManager.verifyPinCode("1235")).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
fun createDefaultPinCodeManager(
|
||||
lockScreenStore: LockScreenStore = InMemoryLockScreenStore(),
|
||||
secretKeyRepository: SecretKeyRepository = SimpleSecretKeyRepository(),
|
||||
encryptionDecryptionService: EncryptionDecryptionService = AESEncryptionDecryptionService(),
|
||||
) = DefaultPinCodeManager(
|
||||
lockScreenStore = lockScreenStore,
|
||||
secretKeyRepository = secretKeyRepository,
|
||||
encryptionDecryptionService = encryptionDecryptionService,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue