From f1b9ded3e3e957d692c3979cb921f10d2575bb0f Mon Sep 17 00:00:00 2001 From: Brian Yencho Date: Sun, 14 Jan 2024 08:19:11 -0600 Subject: [PATCH] Add clearData calls DiskSources (#603) --- .../auth/datasource/disk/AuthDiskSource.kt | 7 +++ .../datasource/disk/AuthDiskSourceImpl.kt | 13 ++++- .../datasource/disk/SettingsDiskSource.kt | 5 ++ .../datasource/disk/SettingsDiskSourceImpl.kt | 5 ++ .../repository/SettingsRepositoryImpl.kt | 11 +--- .../datasource/disk/GeneratorDiskSource.kt | 4 ++ .../disk/GeneratorDiskSourceImpl.kt | 5 ++ .../datasource/disk/AuthDiskSourceTest.kt | 33 ++++++++++++ .../disk/util/FakeAuthDiskSource.kt | 11 ++++ .../datasource/disk/SettingsDiskSourceTest.kt | 18 +++++++ .../disk/util/FakeSettingsDiskSource.kt | 8 +++ .../disk/GeneratorDiskSourceTest.kt | 53 +++++++++++++++++++ 12 files changed, 161 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt index 2a2745b93b..8d27cfe610 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt @@ -31,6 +31,13 @@ interface AuthDiskSource { */ val userStateFlow: Flow + /** + * Clears all the settings data for the given user. + * + * Note that this does not include any data saved in the [userState]. + */ + fun clearData(userId: String) + /** * Retrieves the "last active time" for the given [userId], in milliseconds. * diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt index e5ef1761e9..dbfc020309 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt @@ -67,6 +67,17 @@ class AuthDiskSourceImpl( get() = mutableUserStateFlow .onSubscription { emit(userState) } + private val mutableUserStateFlow = bufferedMutableSharedFlow(replay = 1) + + override fun clearData(userId: String) { + storeLastActiveTimeMillis(userId = userId, lastActiveTimeMillis = null) + storeUserKey(userId = userId, userKey = null) + storeUserAutoUnlockKey(userId = userId, userAutoUnlockKey = null) + storePrivateKey(userId = userId, privateKey = null) + storeOrganizationKeys(userId = userId, organizationKeys = null) + storeOrganizations(userId = userId, organizations = null) + } + override fun getLastActiveTimeMillis(userId: String): Long? = getLong(key = "${LAST_ACTIVE_TIME_KEY}_$userId") @@ -80,8 +91,6 @@ class AuthDiskSourceImpl( ) } - private val mutableUserStateFlow = bufferedMutableSharedFlow(replay = 1) - override fun getUserKey(userId: String): String? = getString(key = "${MASTER_KEY_ENCRYPTION_USER_KEY}_$userId") diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt index 2341f26931..f3be128fef 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt @@ -13,6 +13,11 @@ interface SettingsDiskSource { */ var appLanguage: AppLanguage? + /** + * Clears all the settings data for the given user. + */ + fun clearData(userId: String) + /** * Gets the current vault timeout (in minutes) for the given [userId] (or `null` if the vault * should never time out). diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt index 715b38cfb7..1754662e2e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt @@ -39,6 +39,11 @@ class SettingsDiskSourceImpl( ) } + override fun clearData(userId: String) { + storeVaultTimeoutInMinutes(userId = userId, vaultTimeoutInMinutes = null) + storeVaultTimeoutAction(userId = userId, vaultTimeoutAction = null) + } + override fun getVaultTimeoutInMinutes(userId: String): Int? = getInt(key = "${VAULT_TIME_IN_MINUTES_KEY}_$userId") diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt index b7d0f990c9..3fc67efe04 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt @@ -59,16 +59,7 @@ class SettingsRepositoryImpl( } override fun clearData(userId: String) { - settingsDiskSource.apply { - storeVaultTimeoutInMinutes( - userId = userId, - vaultTimeoutInMinutes = null, - ) - storeVaultTimeoutAction( - userId = userId, - vaultTimeoutAction = null, - ) - } + settingsDiskSource.clearData(userId = userId) } override fun setDefaultsIfNecessary(userId: String) { diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt index 2c9a6504ac..ffec3eb706 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt @@ -7,6 +7,10 @@ import com.x8bit.bitwarden.data.tools.generator.repository.model.UsernameGenerat * Primary access point for disk information related to generation. */ interface GeneratorDiskSource { + /** + * Clears all the settings data for the given user. + */ + fun clearData(userId: String) /** * Retrieves a user's passcode generation options using a [userId]. diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt index 877c458313..0fcdc0d098 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt @@ -19,6 +19,11 @@ class GeneratorDiskSourceImpl( ) : BaseDiskSource(sharedPreferences), GeneratorDiskSource { + override fun clearData(userId: String) { + storePasscodeGenerationOptions(userId = userId, options = null) + storeUsernameGenerationOptions(userId = userId, options = null) + } + override fun getPasscodeGenerationOptions(userId: String): PasscodeGenerationOptions? { val key = getPasswordGenerationOptionsKey(userId) return getString(key)?.let { json.decodeFromString(it) } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt index 599d3fa513..1ab1d9ada7 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt @@ -118,6 +118,39 @@ class AuthDiskSourceTest { } } + @Test + fun `clearData should clear all necessary data for the given user`() { + val userId = "userId" + + authDiskSource.storeLastActiveTimeMillis( + userId = userId, + lastActiveTimeMillis = 123456789L, + ) + authDiskSource.storeUserKey(userId = userId, userKey = "userKey") + authDiskSource.storeUserAutoUnlockKey( + userId = userId, + userAutoUnlockKey = "userAutoUnlockKey", + ) + authDiskSource.storePrivateKey(userId = userId, privateKey = "privateKey") + authDiskSource.storeOrganizationKeys( + userId = userId, + organizationKeys = mapOf("organizationId" to "key"), + ) + authDiskSource.storeOrganizations( + userId = userId, + organizations = listOf(createMockOrganization(1)), + ) + + authDiskSource.clearData(userId = userId) + + assertNull(authDiskSource.getLastActiveTimeMillis(userId = userId)) + assertNull(authDiskSource.getUserKey(userId = userId)) + assertNull(authDiskSource.getUserAutoUnlockKey(userId = userId)) + assertNull(authDiskSource.getPrivateKey(userId = userId)) + assertNull(authDiskSource.getOrganizationKeys(userId = userId)) + assertNull(authDiskSource.getOrganizations(userId = userId)) + } + @Test fun `getLastActiveTimeMillis should pull from SharedPreferences`() { val lastActiveTimeBaseKey = "bwPreferencesStorage:lastActiveTime" diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt index 1f33c63001..a16184cdef 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt @@ -36,6 +36,17 @@ class FakeAuthDiskSource : AuthDiskSource { override val userStateFlow: Flow get() = mutableUserStateFlow.onSubscription { emit(userState) } + override fun clearData(userId: String) { + storedLastActiveTimeMillis.remove(userId) + storedUserKeys.remove(userId) + storedPrivateKeys.remove(userId) + storedUserAutoUnlockKeys.remove(userId) + storedOrganizations.remove(userId) + + storedOrganizationKeys.remove(userId) + mutableOrganizationsFlowMap.remove(userId) + } + override fun getLastActiveTimeMillis(userId: String): Long? = storedLastActiveTimeMillis[userId] diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt index ac7494114c..28c4df1c2d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt @@ -58,6 +58,24 @@ class SettingsDiskSourceTest { ) } + @Test + fun `clearData should clear all necessary data for the given user`() { + val userId = "userId" + settingsDiskSource.storeVaultTimeoutInMinutes( + userId = userId, + vaultTimeoutInMinutes = 30, + ) + settingsDiskSource.storeVaultTimeoutAction( + userId = userId, + vaultTimeoutAction = VaultTimeoutAction.LOCK, + ) + + settingsDiskSource.clearData(userId = userId) + + assertNull(settingsDiskSource.getVaultTimeoutInMinutes(userId = userId)) + assertNull(settingsDiskSource.getVaultTimeoutAction(userId = userId)) + } + @Test fun `getVaultTimeoutInMinutes when values are present should pull from SharedPreferences`() { val vaultTimeoutBaseKey = "bwPreferencesStorage:vaultTimeout" diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt index 338df7eac6..c98fa13a7a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt @@ -24,6 +24,14 @@ class FakeSettingsDiskSource : SettingsDiskSource { override var appLanguage: AppLanguage? = null + override fun clearData(userId: String) { + storedVaultTimeoutActions.remove(userId) + storedVaultTimeoutInMinutes.remove(userId) + + mutableVaultTimeoutActionsFlowMap.remove(userId) + mutableVaultTimeoutInMinutesFlowMap.remove(userId) + } + override fun getVaultTimeoutInMinutes(userId: String): Int? = storedVaultTimeoutInMinutes[userId] diff --git a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt index 54779e5916..274956af02 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt @@ -25,6 +25,59 @@ class GeneratorDiskSourceTest { json = json, ) + @Test + fun `clearData should clear all necessary data for the given user`() { + val userId = "userId" + + val passcodeOptions = PasscodeGenerationOptions( + length = 14, + allowAmbiguousChar = false, + hasNumbers = true, + minNumber = 0, + hasUppercase = true, + minUppercase = null, + hasLowercase = false, + minLowercase = null, + allowSpecial = false, + minSpecial = 1, + allowCapitalize = false, + allowIncludeNumber = false, + wordSeparator = "-", + numWords = 3, + ) + val userNameOptions = UsernameGenerationOptions( + type = UsernameGenerationOptions.UsernameType.RANDOM_WORD, + serviceType = UsernameGenerationOptions.ForwardedEmailServiceType.NONE, + capitalizeRandomWordUsername = true, + includeNumberRandomWordUsername = false, + plusAddressedEmail = "example+plus@gmail.com", + catchAllEmailDomain = "example.com", + firefoxRelayApiAccessToken = "access_token_firefox_relay", + simpleLoginApiKey = "api_key_simple_login", + duckDuckGoApiKey = "api_key_duck_duck_go", + fastMailApiKey = "api_key_fast_mail", + anonAddyApiAccessToken = "access_token_anon_addy", + anonAddyDomainName = "anonaddy.com", + forwardEmailApiAccessToken = "access_token_forward_email", + forwardEmailDomainName = "forwardemail.net", + emailWebsite = "email.example.com", + ) + + generatorDiskSource.storePasscodeGenerationOptions( + userId = userId, + options = passcodeOptions, + ) + generatorDiskSource.storeUsernameGenerationOptions( + userId = userId, + options = userNameOptions, + ) + + generatorDiskSource.clearData(userId = userId) + + assertNull(generatorDiskSource.getPasscodeGenerationOptions(userId = userId)) + assertNull(generatorDiskSource.getUsernameGenerationOptions(userId = userId)) + } + @Test fun `getPasscodeGenerationOptions should return correct options when available`() { val userId = "user123"