mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
Add VaultTimeoutAction and handle its persistence (#520)
This commit is contained in:
parent
6acfb10709
commit
e2c35fc373
5 changed files with 179 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
@ -22,4 +23,23 @@ interface SettingsDiskSource {
|
|||
* Stores the given [vaultTimeoutInMinutes] for the given [userId].
|
||||
*/
|
||||
fun storeVaultTimeoutInMinutes(userId: String, vaultTimeoutInMinutes: Int?)
|
||||
|
||||
/**
|
||||
* Gets the current [VaultTimeoutAction] for the given [userId].
|
||||
*/
|
||||
fun getVaultTimeoutAction(userId: String): VaultTimeoutAction?
|
||||
|
||||
/**
|
||||
* Emits updates that track [getVaultTimeoutAction] for the given [userId]. This will replay
|
||||
* the last known value, if any.
|
||||
*/
|
||||
fun getVaultTimeoutActionFlow(userId: String): Flow<VaultTimeoutAction?>
|
||||
|
||||
/**
|
||||
* Stores the given [vaultTimeoutAction] for the given [userId].
|
||||
*/
|
||||
fun storeVaultTimeoutAction(
|
||||
userId: String,
|
||||
vaultTimeoutAction: VaultTimeoutAction?,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@ package com.x8bit.bitwarden.data.platform.datasource.disk
|
|||
|
||||
import android.content.SharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource.Companion.BASE_KEY
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.onSubscription
|
||||
|
||||
private const val VAULT_TIMEOUT_ACTION_KEY = "$BASE_KEY:vaultTimeoutAction"
|
||||
private const val VAULT_TIME_IN_MINUTES_KEY = "$BASE_KEY:vaultTimeout"
|
||||
|
||||
/**
|
||||
|
@ -16,6 +18,9 @@ class SettingsDiskSourceImpl(
|
|||
val sharedPreferences: SharedPreferences,
|
||||
) : BaseDiskSource(sharedPreferences = sharedPreferences),
|
||||
SettingsDiskSource {
|
||||
private val mutableVaultTimeoutActionFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<VaultTimeoutAction?>>()
|
||||
|
||||
private val mutableVaultTimeoutInMinutesFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<Int?>>()
|
||||
|
||||
|
@ -37,6 +42,33 @@ class SettingsDiskSourceImpl(
|
|||
getMutableVaultTimeoutInMinutesFlow(userId = userId).tryEmit(vaultTimeoutInMinutes)
|
||||
}
|
||||
|
||||
override fun getVaultTimeoutAction(userId: String): VaultTimeoutAction? =
|
||||
getInt(key = "${VAULT_TIMEOUT_ACTION_KEY}_$userId")?.let { storedValue ->
|
||||
VaultTimeoutAction.entries.firstOrNull { storedValue == it.value }
|
||||
}
|
||||
|
||||
override fun getVaultTimeoutActionFlow(userId: String): Flow<VaultTimeoutAction?> =
|
||||
getMutableVaultTimeoutActionFlow(userId = userId)
|
||||
.onSubscription { emit(getVaultTimeoutAction(userId = userId)) }
|
||||
|
||||
override fun storeVaultTimeoutAction(
|
||||
userId: String,
|
||||
vaultTimeoutAction: VaultTimeoutAction?,
|
||||
) {
|
||||
putInt(
|
||||
key = "${VAULT_TIMEOUT_ACTION_KEY}_$userId",
|
||||
value = vaultTimeoutAction?.value,
|
||||
)
|
||||
getMutableVaultTimeoutActionFlow(userId = userId).tryEmit(vaultTimeoutAction)
|
||||
}
|
||||
|
||||
private fun getMutableVaultTimeoutActionFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<VaultTimeoutAction?> =
|
||||
mutableVaultTimeoutActionFlowMap.getOrPut(userId) {
|
||||
bufferedMutableSharedFlow(replay = 1)
|
||||
}
|
||||
|
||||
private fun getMutableVaultTimeoutInMinutesFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<Int?> =
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.x8bit.bitwarden.data.platform.repository.model
|
||||
|
||||
/**
|
||||
* Represents different type of actions that may be performed when a vault times out.
|
||||
*
|
||||
* The [value] is used for consistent storage purposes.
|
||||
*/
|
||||
enum class VaultTimeoutAction(
|
||||
val value: Int,
|
||||
) {
|
||||
/**
|
||||
* The vault should lock when it times out.
|
||||
*/
|
||||
LOCK(0),
|
||||
|
||||
/**
|
||||
* The user should be logged out when their vault times out.
|
||||
*/
|
||||
LOGOUT(1),
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.platform.datasource.disk
|
|||
import androidx.core.content.edit
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeSharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
|
@ -95,4 +96,83 @@ class SettingsDiskSourceTest {
|
|||
)
|
||||
assertFalse(fakeSharedPreferences.contains(vaultTimeoutKey))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getVaultTimeoutAction when values are present should pull from SharedPreferences`() {
|
||||
val vaultTimeoutActionBaseKey = "bwPreferencesStorage:vaultTimeoutAction"
|
||||
val mockUserId = "mockUserId"
|
||||
val vaultTimeoutAction = VaultTimeoutAction.LOCK
|
||||
fakeSharedPreferences
|
||||
.edit()
|
||||
.putInt(
|
||||
"${vaultTimeoutActionBaseKey}_$mockUserId",
|
||||
vaultTimeoutAction.value,
|
||||
)
|
||||
.apply()
|
||||
val actual = settingsDiskSource.getVaultTimeoutAction(userId = mockUserId)
|
||||
assertEquals(
|
||||
vaultTimeoutAction,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getVaultTimeoutAction when values are absent should return null`() {
|
||||
val mockUserId = "mockUserId"
|
||||
assertNull(settingsDiskSource.getVaultTimeoutAction(userId = mockUserId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getVaultTimeoutActionFlow should react to changes in getOrganizations`() = runTest {
|
||||
val mockUserId = "mockUserId"
|
||||
val vaultTimeoutAction = VaultTimeoutAction.LOCK
|
||||
settingsDiskSource.getVaultTimeoutActionFlow(userId = mockUserId).test {
|
||||
// The initial values of the Flow and the property are in sync
|
||||
assertNull(settingsDiskSource.getVaultTimeoutAction(userId = mockUserId))
|
||||
assertNull(awaitItem())
|
||||
|
||||
// Updating the disk source updates shared preferences
|
||||
settingsDiskSource.storeVaultTimeoutAction(
|
||||
userId = mockUserId,
|
||||
vaultTimeoutAction = vaultTimeoutAction,
|
||||
)
|
||||
assertEquals(vaultTimeoutAction, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeVaultTimeoutAction for non-null values should update SharedPreferences`() {
|
||||
val vaultTimeoutActionBaseKey = "bwPreferencesStorage:vaultTimeoutAction"
|
||||
val mockUserId = "mockUserId"
|
||||
val vaultTimeoutAction = VaultTimeoutAction.LOCK
|
||||
settingsDiskSource.storeVaultTimeoutAction(
|
||||
userId = mockUserId,
|
||||
vaultTimeoutAction = vaultTimeoutAction,
|
||||
)
|
||||
val actual = fakeSharedPreferences.getInt(
|
||||
"${vaultTimeoutActionBaseKey}_$mockUserId",
|
||||
0,
|
||||
)
|
||||
assertEquals(
|
||||
vaultTimeoutAction.value,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storeVaultTimeoutAction for null values should clear SharedPreferences`() {
|
||||
val vaultTimeoutActionBaseKey = "bwPreferencesStorage:vaultTimeoutAction"
|
||||
val mockUserId = "mockUserId"
|
||||
val previousValue = VaultTimeoutAction.LOCK
|
||||
val vaultTimeoutActionKey = "${vaultTimeoutActionBaseKey}_$mockUserId"
|
||||
fakeSharedPreferences.edit {
|
||||
putInt(vaultTimeoutActionKey, previousValue.value)
|
||||
}
|
||||
assertTrue(fakeSharedPreferences.contains(vaultTimeoutActionKey))
|
||||
settingsDiskSource.storeVaultTimeoutAction(
|
||||
userId = mockUserId,
|
||||
vaultTimeoutAction = null,
|
||||
)
|
||||
assertFalse(fakeSharedPreferences.contains(vaultTimeoutActionKey))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk.util
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
@ -11,9 +12,13 @@ import kotlinx.coroutines.flow.onSubscription
|
|||
*/
|
||||
class FakeSettingsDiskSource : SettingsDiskSource {
|
||||
|
||||
private val mutableVaultTimeoutActionsFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<VaultTimeoutAction?>>()
|
||||
|
||||
private val mutableVaultTimeoutInMinutesFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<Int?>>()
|
||||
|
||||
private val storedVaultTimeoutActions = mutableMapOf<String, VaultTimeoutAction?>()
|
||||
private val storedVaultTimeoutInMinutes = mutableMapOf<String, Int?>()
|
||||
|
||||
override fun getVaultTimeoutInMinutes(userId: String): Int? =
|
||||
|
@ -31,8 +36,30 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
getMutableVaultTimeoutInMinutesFlow(userId = userId).tryEmit(vaultTimeoutInMinutes)
|
||||
}
|
||||
|
||||
override fun getVaultTimeoutAction(userId: String): VaultTimeoutAction? =
|
||||
storedVaultTimeoutActions[userId]
|
||||
|
||||
override fun getVaultTimeoutActionFlow(userId: String): Flow<VaultTimeoutAction?> =
|
||||
getMutableVaultTimeoutActionsFlow(userId = userId)
|
||||
.onSubscription { emit(getVaultTimeoutAction(userId = userId)) }
|
||||
|
||||
override fun storeVaultTimeoutAction(
|
||||
userId: String,
|
||||
vaultTimeoutAction: VaultTimeoutAction?,
|
||||
) {
|
||||
storedVaultTimeoutActions[userId] = vaultTimeoutAction
|
||||
getMutableVaultTimeoutActionsFlow(userId = userId).tryEmit(vaultTimeoutAction)
|
||||
}
|
||||
|
||||
//region Private helper functions
|
||||
|
||||
private fun getMutableVaultTimeoutActionsFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<VaultTimeoutAction?> =
|
||||
mutableVaultTimeoutActionsFlowMap.getOrPut(userId) {
|
||||
bufferedMutableSharedFlow(replay = 1)
|
||||
}
|
||||
|
||||
private fun getMutableVaultTimeoutInMinutesFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<Int?> =
|
||||
|
|
Loading…
Add table
Reference in a new issue