[PM-27120] cxp hide user account when remove individual export is enabled (#6089)

This commit is contained in:
aj-rosado 2025-10-31 10:08:24 +00:00 committed by GitHub
parent dbf2e9f68a
commit d07b119802
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 553 additions and 43 deletions

View File

@ -1,6 +1,8 @@
package com.x8bit.bitwarden.data.auth.manager package com.x8bit.bitwarden.data.auth.manager
import com.bitwarden.data.manager.DispatcherManager import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
@ -19,6 +21,7 @@ import com.x8bit.bitwarden.data.auth.repository.util.userKeyConnectorStateList
import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsList import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsList
import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsListFlow import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsListFlow
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
@ -39,6 +42,7 @@ class UserStateManagerImpl(
private val authDiskSource: AuthDiskSource, private val authDiskSource: AuthDiskSource,
firstTimeActionManager: FirstTimeActionManager, firstTimeActionManager: FirstTimeActionManager,
vaultLockManager: VaultLockManager, vaultLockManager: VaultLockManager,
private val policyManager: PolicyManager,
dispatcherManager: DispatcherManager, dispatcherManager: DispatcherManager,
) : UserStateManager { ) : UserStateManager {
private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined) private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined)
@ -110,6 +114,7 @@ class UserStateManagerImpl(
vaultUnlockTypeProvider = ::getVaultUnlockType, vaultUnlockTypeProvider = ::getVaultUnlockType,
isDeviceTrustedProvider = ::isDeviceTrusted, isDeviceTrustedProvider = ::isDeviceTrusted,
firstTimeState = firstTimeState, firstTimeState = firstTimeState,
getUserPolicies = ::existingPolicies,
) )
} }
.filterNot { .filterNot {
@ -133,6 +138,7 @@ class UserStateManagerImpl(
vaultUnlockTypeProvider = ::getVaultUnlockType, vaultUnlockTypeProvider = ::getVaultUnlockType,
isDeviceTrustedProvider = ::isDeviceTrusted, isDeviceTrustedProvider = ::isDeviceTrusted,
firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState, firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState,
getUserPolicies = ::existingPolicies,
), ),
) )
@ -159,4 +165,12 @@ class UserStateManagerImpl(
.getPinProtectedUserKeyEnvelope(userId = userId) .getPinProtectedUserKeyEnvelope(userId = userId)
?.let { VaultUnlockType.PIN } ?.let { VaultUnlockType.PIN }
?: VaultUnlockType.MASTER_PASSWORD ?: VaultUnlockType.MASTER_PASSWORD
private fun existingPolicies(
userId: String,
policyType: PolicyTypeJson,
): List<SyncResponseJson.Policy> = policyManager.getUserPolicies(
userId = userId,
type = policyType,
)
} }

View File

@ -102,11 +102,13 @@ object AuthRepositoryModule {
authDiskSource: AuthDiskSource, authDiskSource: AuthDiskSource,
firstTimeActionManager: FirstTimeActionManager, firstTimeActionManager: FirstTimeActionManager,
vaultLockManager: VaultLockManager, vaultLockManager: VaultLockManager,
policyManager: PolicyManager,
dispatcherManager: DispatcherManager, dispatcherManager: DispatcherManager,
): UserStateManager = UserStateManagerImpl( ): UserStateManager = UserStateManagerImpl(
authDiskSource = authDiskSource, authDiskSource = authDiskSource,
firstTimeActionManager = firstTimeActionManager, firstTimeActionManager = firstTimeActionManager,
vaultLockManager = vaultLockManager, vaultLockManager = vaultLockManager,
policyManager = policyManager,
dispatcherManager = dispatcherManager, dispatcherManager = dispatcherManager,
) )
} }

View File

@ -75,6 +75,7 @@ data class UserState(
val isUsingKeyConnector: Boolean, val isUsingKeyConnector: Boolean,
val onboardingStatus: OnboardingStatus, val onboardingStatus: OnboardingStatus,
val firstTimeState: FirstTimeState, val firstTimeState: FirstTimeState,
val isExportable: Boolean,
) { ) {
/** /**
* Indicates that the user does or does not have a means to manually unlock the vault. * Indicates that the user does or does not have a means to manually unlock the vault.

View File

@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.auth.repository.util
import com.bitwarden.data.repository.util.toEnvironmentUrlsOrDefault import com.bitwarden.data.repository.util.toEnvironmentUrlsOrDefault
import com.bitwarden.network.model.KdfTypeJson import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.OrganizationType import com.bitwarden.network.model.OrganizationType
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.UserDecryptionOptionsJson import com.bitwarden.network.model.UserDecryptionOptionsJson
import com.bitwarden.ui.platform.base.util.toHexColorRepresentation import com.bitwarden.ui.platform.base.util.toHexColorRepresentation
@ -164,6 +165,7 @@ fun UserStateJson.toUserState(
isBiometricsEnabledProvider: (userId: String) -> Boolean, isBiometricsEnabledProvider: (userId: String) -> Boolean,
vaultUnlockTypeProvider: (userId: String) -> VaultUnlockType, vaultUnlockTypeProvider: (userId: String) -> VaultUnlockType,
isDeviceTrustedProvider: (userId: String) -> Boolean, isDeviceTrustedProvider: (userId: String) -> Boolean,
getUserPolicies: (userId: String, policy: PolicyTypeJson) -> List<SyncResponseJson.Policy>,
): UserState = ): UserState =
UserState( UserState(
activeUserId = this.activeUserId, activeUserId = this.activeUserId,
@ -203,6 +205,19 @@ fun UserStateJson.toUserState(
hasManageResetPasswordPermission.takeIf { trustedDevice != null } hasManageResetPasswordPermission.takeIf { trustedDevice != null }
val needsMasterPassword = decryptionOptions?.hasMasterPassword == false && val needsMasterPassword = decryptionOptions?.hasMasterPassword == false &&
(tdeUserNeedsMasterPassword ?: (keyConnectorOptions == null)) (tdeUserNeedsMasterPassword ?: (keyConnectorOptions == null))
val hasPersonalOwnershipRestrictedOrg = getUserPolicies(
userId,
PolicyTypeJson.PERSONAL_OWNERSHIP,
)
.any { it.isEnabled }
val hasPersonalVaultExportRestrictedOrg = getUserPolicies(
userId,
PolicyTypeJson.DISABLE_PERSONAL_VAULT_EXPORT,
)
.any { it.isEnabled }
UserState.Account( UserState.Account(
userId = userId, userId = userId,
name = profile.name, name = profile.name,
@ -231,6 +246,8 @@ fun UserStateJson.toUserState(
// using the app prior to the release of the onboarding flow. // using the app prior to the release of the onboarding flow.
onboardingStatus = onboardingStatus ?: OnboardingStatus.COMPLETE, onboardingStatus = onboardingStatus ?: OnboardingStatus.COMPLETE,
firstTimeState = firstTimeState, firstTimeState = firstTimeState,
isExportable = !hasPersonalOwnershipRestrictedOrg &&
!hasPersonalVaultExportRestrictedOrg,
) )
}, },
hasPendingAccountAddition = hasPendingAccountAddition, hasPendingAccountAddition = hasPendingAccountAddition,

View File

@ -17,4 +17,12 @@ interface PolicyManager {
* Get all the policies of the given [type] that are enabled and applicable to the user. * Get all the policies of the given [type] that are enabled and applicable to the user.
*/ */
fun getActivePolicies(type: PolicyTypeJson): List<SyncResponseJson.Policy> fun getActivePolicies(type: PolicyTypeJson): List<SyncResponseJson.Policy>
/**
* Get all the policies of the given [type] that are enabled and applicable to the [userId].
*/
fun getUserPolicies(
userId: String,
type: PolicyTypeJson,
): List<SyncResponseJson.Policy>
} }

View File

@ -54,6 +54,18 @@ class PolicyManagerImpl(
} }
?: emptyList() ?: emptyList()
override fun getUserPolicies(
userId: String,
type: PolicyTypeJson,
): List<SyncResponseJson.Policy> =
this
.filterPolicies(
userId = userId,
type = type,
policies = authDiskSource.getPolicies(userId = userId),
)
.orEmpty()
/** /**
* A helper method to filter policies. * A helper method to filter policies.
*/ */

View File

@ -315,6 +315,15 @@ class VaultSyncManagerImpl(
} }
} }
// Treat absent network policies as known empty data to
// distinguish between unknown null data.
// The user state update will trigger flows that depend on the latest policies.
// We must store the new policies first to prevent old data on UserState.
authDiskSource.storePolicies(
userId = userId,
policies = syncResponse.policies.orEmpty(),
)
// Update user information with additional information from sync response // Update user information with additional information from sync response
authDiskSource.userState = authDiskSource.userState?.toUpdatedUserStateJson( authDiskSource.userState = authDiskSource.userState?.toUpdatedUserStateJson(
syncResponse = syncResponse, syncResponse = syncResponse,
@ -323,12 +332,6 @@ class VaultSyncManagerImpl(
unlockVaultForOrganizationsIfNecessary(syncResponse = syncResponse) unlockVaultForOrganizationsIfNecessary(syncResponse = syncResponse)
storeProfileData(syncResponse = syncResponse) storeProfileData(syncResponse = syncResponse)
// Treat absent network policies as known empty data to
// distinguish between unknown null data.
authDiskSource.storePolicies(
userId = userId,
policies = syncResponse.policies.orEmpty(),
)
settingsDiskSource.storeLastSyncTime( settingsDiskSource.storeLastSyncTime(
userId = userId, userId = userId,
lastSyncTime = clock.instant(), lastSyncTime = clock.instant(),

View File

@ -297,6 +297,7 @@ fun RootNavScreen(
navController.navigateToVerifyPassword( navController.navigateToVerifyPassword(
userId = currentState.userId, userId = currentState.userId,
navOptions = rootNavOptions, navOptions = rootNavOptions,
hasOtherAccounts = false,
) )
} }
} }

View File

@ -89,9 +89,10 @@ class RootNavViewModel @Inject constructor(
} }
specialCircumstance is SpecialCircumstance.CredentialExchangeExport -> { specialCircumstance is SpecialCircumstance.CredentialExchangeExport -> {
if (userState.accounts.size == 1) { val exportableAccounts = userState.accounts.filter { it.isExportable }
if (exportableAccounts.size == 1) {
RootNavState.CredentialExchangeExportSkipAccountSelection( RootNavState.CredentialExchangeExportSkipAccountSelection(
userId = userState.accounts.first().userId, userId = exportableAccounts.first().userId,
) )
} else { } else {
RootNavState.CredentialExchangeExport RootNavState.CredentialExchangeExport

View File

@ -40,8 +40,11 @@ fun NavGraphBuilder.exportItemsGraph(
startDestination = SelectAccountRoute, startDestination = SelectAccountRoute,
) { ) {
selectAccountDestination( selectAccountDestination(
onAccountSelected = { onAccountSelected = { userId, hasOtherAccounts ->
navController.navigateToVerifyPassword(userId = it) navController.navigateToVerifyPassword(
userId = userId,
hasOtherAccounts = hasOtherAccounts,
)
}, },
) )
verifyPasswordDestination( verifyPasswordDestination(

View File

@ -20,7 +20,7 @@ data object SelectAccountRoute
* Add the [SelectAccountScreen] to the nav graph. * Add the [SelectAccountScreen] to the nav graph.
*/ */
fun NavGraphBuilder.selectAccountDestination( fun NavGraphBuilder.selectAccountDestination(
onAccountSelected: (id: String) -> Unit, onAccountSelected: (id: String, hasOtherAccounts: Boolean) -> Unit,
) { ) {
composableWithRootPushTransitions<SelectAccountRoute> { composableWithRootPushTransitions<SelectAccountRoute> {
SelectAccountScreen( SelectAccountScreen(

View File

@ -51,7 +51,7 @@ import kotlinx.collections.immutable.persistentListOf
@Composable @Composable
@Suppress("LongMethod") @Suppress("LongMethod")
fun SelectAccountScreen( fun SelectAccountScreen(
onAccountSelected: (userId: String) -> Unit, onAccountSelected: (userId: String, hasOtherAccounts: Boolean) -> Unit,
viewModel: SelectAccountViewModel = hiltViewModel(), viewModel: SelectAccountViewModel = hiltViewModel(),
credentialExchangeCompletionManager: CredentialExchangeCompletionManager = credentialExchangeCompletionManager: CredentialExchangeCompletionManager =
LocalCredentialExchangeCompletionManager.current, LocalCredentialExchangeCompletionManager.current,
@ -74,7 +74,7 @@ fun SelectAccountScreen(
} }
is SelectAccountEvent.NavigateToPasswordVerification -> { is SelectAccountEvent.NavigateToPasswordVerification -> {
onAccountSelected(event.userId) onAccountSelected(event.userId, event.hasOtherAccounts)
} }
is SelectAccountEvent.ValidateImportRequest -> { is SelectAccountEvent.ValidateImportRequest -> {

View File

@ -99,8 +99,9 @@ class SelectAccountViewModel @Inject constructor(
private fun handleAccountClick(action: SelectAccountAction.AccountClick) { private fun handleAccountClick(action: SelectAccountAction.AccountClick) {
sendEvent( sendEvent(
SelectAccountEvent.NavigateToPasswordVerification( event = SelectAccountEvent.NavigateToPasswordVerification(
action.userId, userId = action.userId,
hasOtherAccounts = true,
), ),
) )
} }
@ -119,19 +120,14 @@ class SelectAccountViewModel @Inject constructor(
val itemRestrictedOrgIds = action.itemRestrictedOrgs val itemRestrictedOrgIds = action.itemRestrictedOrgs
.filter { it.isEnabled } .filter { it.isEnabled }
.map { it.organizationId } .map { it.organizationId }
val personalOwnershipRestrictedOrgIds = action
.personalOwnershipOrgs
.filter { it.isEnabled }
.map { it.organizationId }
val accountSelectionListItems = action.userState val accountSelectionListItems = action.userState
?.accounts ?.accounts
.orEmpty() .orEmpty()
// We only want accounts that do not restrict personal vault ownership // We only want accounts that do not restrict personal vault ownership
// or vault export
.filter { account -> .filter { account ->
account account.isExportable
.organizations
.none { org -> org.id in personalOwnershipRestrictedOrgIds }
} }
.map { account -> .map { account ->
AccountSelectionListItem( AccountSelectionListItem(
@ -164,12 +160,10 @@ class SelectAccountViewModel @Inject constructor(
combine( combine(
authRepository.userStateFlow, authRepository.userStateFlow,
policyManager.getActivePoliciesFlow(PolicyTypeJson.RESTRICT_ITEM_TYPES), policyManager.getActivePoliciesFlow(PolicyTypeJson.RESTRICT_ITEM_TYPES),
policyManager.getActivePoliciesFlow(PolicyTypeJson.PERSONAL_OWNERSHIP), ) { userState, itemRestrictedOrgs ->
) { userState, itemRestrictedOrgs, personalOwnershipOrgs ->
SelectAccountAction.Internal.SelectionDataReceive( SelectAccountAction.Internal.SelectionDataReceive(
userState = userState, userState = userState,
itemRestrictedOrgs = itemRestrictedOrgs, itemRestrictedOrgs = itemRestrictedOrgs,
personalOwnershipOrgs = personalOwnershipOrgs,
) )
} }
.onEach(::handleAction) .onEach(::handleAction)
@ -255,7 +249,6 @@ sealed class SelectAccountAction {
data class SelectionDataReceive( data class SelectionDataReceive(
val userState: UserState?, val userState: UserState?,
val itemRestrictedOrgs: List<SyncResponseJson.Policy>, val itemRestrictedOrgs: List<SyncResponseJson.Policy>,
val personalOwnershipOrgs: List<SyncResponseJson.Policy>,
) : Internal() ) : Internal()
} }
} }
@ -282,5 +275,6 @@ sealed class SelectAccountEvent {
*/ */
data class NavigateToPasswordVerification( data class NavigateToPasswordVerification(
val userId: String, val userId: String,
val hasOtherAccounts: Boolean,
) : SelectAccountEvent() ) : SelectAccountEvent()
} }

View File

@ -22,6 +22,7 @@ import kotlinx.serialization.Serializable
@OmitFromCoverage @OmitFromCoverage
data class VerifyPasswordRoute( data class VerifyPasswordRoute(
val userId: String, val userId: String,
val hasOtherAccounts: Boolean,
) : Parcelable { ) : Parcelable {
/** /**
@ -38,6 +39,7 @@ data class VerifyPasswordRoute(
@OmitFromCoverage @OmitFromCoverage
data class VerifyPasswordArgs( data class VerifyPasswordArgs(
val userId: String, val userId: String,
val hasOtherAccounts: Boolean,
) )
/** /**
@ -47,6 +49,7 @@ fun SavedStateHandle.toVerifyPasswordArgs(): VerifyPasswordArgs {
val route = this.toRoute<VerifyPasswordRoute>() val route = this.toRoute<VerifyPasswordRoute>()
return VerifyPasswordArgs( return VerifyPasswordArgs(
userId = route.userId, userId = route.userId,
hasOtherAccounts = route.hasOtherAccounts,
) )
} }
@ -70,10 +73,14 @@ fun NavGraphBuilder.verifyPasswordDestination(
*/ */
fun NavController.navigateToVerifyPassword( fun NavController.navigateToVerifyPassword(
userId: String, userId: String,
hasOtherAccounts: Boolean,
navOptions: NavOptions? = null, navOptions: NavOptions? = null,
) { ) {
navigate( navigate(
route = VerifyPasswordRoute(userId = userId), route = VerifyPasswordRoute(
userId = userId,
hasOtherAccounts = hasOtherAccounts,
),
navOptions = navOptions, navOptions = navOptions,
) )
} }

View File

@ -56,11 +56,7 @@ class VerifyPasswordViewModel @Inject constructor(
?.firstOrNull { it.userId == args.userId } ?.firstOrNull { it.userId == args.userId }
?: throw IllegalStateException("Account not found") ?: throw IllegalStateException("Account not found")
val singleAccount = authRepository val singleAccount = !args.hasOtherAccounts
.userStateFlow
.value
?.accounts
?.size == 1
val restrictedItemPolicyOrgIds = policyManager val restrictedItemPolicyOrgIds = policyManager
.getActivePolicies(PolicyTypeJson.RESTRICT_ITEM_TYPES) .getActivePolicies(PolicyTypeJson.RESTRICT_ITEM_TYPES)

View File

@ -1281,6 +1281,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = DEFAULT_FIRST_TIME_STATE, firstTimeState = DEFAULT_FIRST_TIME_STATE,
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -6,6 +6,8 @@ import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
import com.bitwarden.data.manager.DispatcherManager import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.network.model.GetTokenResponseJson import com.bitwarden.network.model.GetTokenResponseJson
import com.bitwarden.network.model.KdfTypeJson import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.createMockOrganization import com.bitwarden.network.model.createMockOrganization
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
@ -16,6 +18,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
import com.x8bit.bitwarden.data.auth.repository.util.toOrganizations import com.x8bit.bitwarden.data.auth.repository.util.toOrganizations
import com.x8bit.bitwarden.data.auth.repository.util.toUserState import com.x8bit.bitwarden.data.auth.repository.util.toUserState
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
@ -48,12 +51,16 @@ class UserStateManagerTest {
every { isActiveUserUnlockingFlow } returns mutableIsActiveUserUnlockingFlow every { isActiveUserUnlockingFlow } returns mutableIsActiveUserUnlockingFlow
} }
private val dispatcherManager: DispatcherManager = FakeDispatcherManager() private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
private val policyManager: PolicyManager = mockk {
every { getUserPolicies(any(), any()) } returns emptyList()
}
private val userStateManager: UserStateManager = UserStateManagerImpl( private val userStateManager: UserStateManager = UserStateManagerImpl(
authDiskSource = fakeAuthDiskSource, authDiskSource = fakeAuthDiskSource,
firstTimeActionManager = firstTimeActionManager, firstTimeActionManager = firstTimeActionManager,
vaultLockManager = vaultLockManager, vaultLockManager = vaultLockManager,
dispatcherManager = dispatcherManager, dispatcherManager = dispatcherManager,
policyManager = policyManager,
) )
@BeforeEach @BeforeEach
@ -87,6 +94,7 @@ class UserStateManagerTest {
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
firstTimeState = FIRST_TIME_STATE, firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
), ),
awaitItem(), awaitItem(),
) )
@ -114,6 +122,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE, firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
), ),
awaitItem(), awaitItem(),
) )
@ -132,6 +141,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE, firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
), ),
awaitItem(), awaitItem(),
) )
@ -162,6 +172,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE, firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
), ),
awaitItem(), awaitItem(),
) )
@ -181,6 +192,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE, firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
) )
val finalUserState = SINGLE_USER_STATE_2.toUserState( val finalUserState = SINGLE_USER_STATE_2.toUserState(
vaultState = VAULT_UNLOCK_DATA, vaultState = VAULT_UNLOCK_DATA,
@ -193,6 +205,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE, firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
) )
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
@ -242,6 +255,41 @@ class UserStateManagerTest {
userStateManager.hasPendingAccountDeletion = false userStateManager.hasPendingAccountDeletion = false
assertFalse(userStateManager.hasPendingAccountDeletion) assertFalse(userStateManager.hasPendingAccountDeletion)
} }
@Test
fun `userStateFlow should update isExportable when getUserPolicies returns policies`() =
runTest {
val policy = SyncResponseJson.Policy(
id = "policyId",
organizationId = "mockId-1",
type = PolicyTypeJson.DISABLE_PERSONAL_VAULT_EXPORT,
data = null,
isEnabled = true,
)
every { policyManager.getUserPolicies(any(), any()) } returns listOf(policy)
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
val userState = SINGLE_USER_STATE_1.toUserState(
vaultState = VAULT_UNLOCK_DATA,
userAccountTokens = emptyList(),
userOrganizationsList = emptyList(),
userIsUsingKeyConnectorList = emptyList(),
hasPendingAccountAddition = false,
isBiometricsEnabledProvider = { false },
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
isDeviceTrustedProvider = { false },
onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> listOf(policy) },
)
// Assert
userStateManager.userStateFlow.test {
val actualItem = awaitItem()
assertEquals(userState, actualItem)
}
}
} }
private const val EMAIL_1 = "test@bitwarden.com" private const val EMAIL_1 = "test@bitwarden.com"

View File

@ -6,6 +6,7 @@ import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.KeyConnectorUserDecryptionOptionsJson import com.bitwarden.network.model.KeyConnectorUserDecryptionOptionsJson
import com.bitwarden.network.model.MasterPasswordUnlockDataJson import com.bitwarden.network.model.MasterPasswordUnlockDataJson
import com.bitwarden.network.model.OrganizationType import com.bitwarden.network.model.OrganizationType
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.TrustedDeviceUserDecryptionOptionsJson import com.bitwarden.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.bitwarden.network.model.UserDecryptionJson import com.bitwarden.network.model.UserDecryptionJson
@ -389,6 +390,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.NOT_STARTED, onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
), ),
@ -462,6 +464,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = OnboardingStatus.NOT_STARTED, onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -502,6 +505,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.NOT_STARTED, onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -571,6 +575,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { false }, isDeviceTrustedProvider = { false },
onboardingStatus = OnboardingStatus.NOT_STARTED, onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -617,6 +622,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true, isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -689,6 +695,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -735,6 +742,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true, isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -807,6 +815,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -853,6 +862,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true, isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -925,6 +935,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -975,6 +986,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true, isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -1047,6 +1059,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -1076,6 +1089,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -1126,6 +1140,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -1155,6 +1170,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true, isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -1207,6 +1223,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -1254,6 +1271,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true, isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -1328,6 +1346,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true }, isDeviceTrustedProvider = { true },
onboardingStatus = null, onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
), ),
) )
} }
@ -1376,6 +1395,7 @@ class UserStateJsonExtensionsTest {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = false, showImportLoginsCard = false,
), ),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -1450,6 +1470,255 @@ class UserStateJsonExtensionsTest {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = false, showImportLoginsCard = false,
), ),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@Test
fun `toUserState isExportable should be false if organization match policies`() {
assertEquals(
UserState(
activeUserId = "activeUserId",
accounts = listOf(
UserState.Account(
userId = "activeUserId",
name = "activeName",
email = "activeEmail",
avatarColorHex = "activeAvatarColorHex",
environment = Environment.Eu,
isPremium = false,
isLoggedIn = true,
isVaultUnlocked = true,
needsPasswordReset = false,
organizations = listOf(
Organization(
id = "organizationId",
name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false,
role = OrganizationType.ADMIN,
keyConnectorUrl = null,
userIsClaimedByOrganization = false,
),
),
isBiometricsEnabled = false,
vaultUnlockType = VaultUnlockType.PIN,
needsMasterPassword = false,
trustedDevice = null,
hasMasterPassword = true,
isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = false,
),
),
),
UserStateJson(
activeUserId = "activeUserId",
accounts = mapOf(
"activeUserId" to AccountJson(
profile = mockk {
every { userId } returns "activeUserId"
every { name } returns "activeName"
every { email } returns "activeEmail"
every { avatarColorHex } returns "activeAvatarColorHex"
every { hasPremium } returns null
every { forcePasswordResetReason } returns null
every { userDecryptionOptions } returns UserDecryptionOptionsJson(
hasMasterPassword = true,
trustedDeviceUserDecryptionOptions = null,
keyConnectorUserDecryptionOptions = null,
masterPasswordUnlock = null,
)
},
tokens = AccountTokensJson(
accessToken = "accessToken",
refreshToken = "refreshToken",
),
settings = AccountJson.Settings(
environmentUrlData = EnvironmentUrlDataJson.DEFAULT_EU,
),
),
),
)
.toUserState(
vaultState = listOf(
VaultUnlockData(
userId = "activeUserId",
status = VaultUnlockData.Status.UNLOCKED,
),
),
userAccountTokens = listOf(
UserAccountTokens(
userId = "activeUserId",
accessToken = "accessToken",
refreshToken = "refreshToken",
),
),
userOrganizationsList = listOf(
UserOrganizations(
userId = "activeUserId",
organizations = listOf(
Organization(
id = "organizationId",
name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false,
role = OrganizationType.ADMIN,
keyConnectorUrl = null,
userIsClaimedByOrganization = false,
),
),
),
),
userIsUsingKeyConnectorList = listOf(
UserKeyConnectorState(
userId = "activeUserId",
isUsingKeyConnector = false,
),
),
hasPendingAccountAddition = false,
isBiometricsEnabledProvider = { false },
vaultUnlockTypeProvider = { VaultUnlockType.PIN },
isDeviceTrustedProvider = { false },
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ ->
listOf(
SyncResponseJson.Policy(
id = "policyId",
organizationId = "organizationId",
type = PolicyTypeJson.DISABLE_PERSONAL_VAULT_EXPORT,
data = null,
isEnabled = true,
),
)
},
),
)
}
@Test
fun `toUserState isExportable should be true if policies is not enabled`() {
assertEquals(
UserState(
activeUserId = "activeUserId",
accounts = listOf(
UserState.Account(
userId = "activeUserId",
name = "activeName",
email = "activeEmail",
avatarColorHex = "activeAvatarColorHex",
environment = Environment.Eu,
isPremium = false,
isLoggedIn = true,
isVaultUnlocked = true,
needsPasswordReset = false,
organizations = listOf(
Organization(
id = "organizationId",
name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false,
role = OrganizationType.ADMIN,
keyConnectorUrl = null,
userIsClaimedByOrganization = false,
),
),
isBiometricsEnabled = false,
vaultUnlockType = VaultUnlockType.PIN,
needsMasterPassword = false,
trustedDevice = null,
hasMasterPassword = true,
isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
),
UserStateJson(
activeUserId = "activeUserId",
accounts = mapOf(
"activeUserId" to AccountJson(
profile = mockk {
every { userId } returns "activeUserId"
every { name } returns "activeName"
every { email } returns "activeEmail"
every { avatarColorHex } returns "activeAvatarColorHex"
every { hasPremium } returns null
every { forcePasswordResetReason } returns null
every { userDecryptionOptions } returns UserDecryptionOptionsJson(
hasMasterPassword = true,
trustedDeviceUserDecryptionOptions = null,
keyConnectorUserDecryptionOptions = null,
masterPasswordUnlock = null,
)
},
tokens = AccountTokensJson(
accessToken = "accessToken",
refreshToken = "refreshToken",
),
settings = AccountJson.Settings(
environmentUrlData = EnvironmentUrlDataJson.DEFAULT_EU,
),
),
),
)
.toUserState(
vaultState = listOf(
VaultUnlockData(
userId = "activeUserId",
status = VaultUnlockData.Status.UNLOCKED,
),
),
userAccountTokens = listOf(
UserAccountTokens(
userId = "activeUserId",
accessToken = "accessToken",
refreshToken = "refreshToken",
),
),
userOrganizationsList = listOf(
UserOrganizations(
userId = "activeUserId",
organizations = listOf(
Organization(
id = "organizationId",
name = "organizationName",
shouldManageResetPassword = false,
shouldUseKeyConnector = false,
role = OrganizationType.ADMIN,
keyConnectorUrl = null,
userIsClaimedByOrganization = false,
),
),
),
),
userIsUsingKeyConnectorList = listOf(
UserKeyConnectorState(
userId = "activeUserId",
isUsingKeyConnector = false,
),
),
hasPendingAccountAddition = false,
isBiometricsEnabledProvider = { false },
vaultUnlockTypeProvider = { VaultUnlockType.PIN },
isDeviceTrustedProvider = { false },
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ ->
listOf(
SyncResponseJson.Policy(
id = "policyId",
organizationId = "organizationId",
type = PolicyTypeJson.DISABLE_PERSONAL_VAULT_EXPORT,
data = null,
isEnabled = false,
),
)
},
), ),
) )
} }

View File

@ -526,6 +526,7 @@ private fun createMockAccounts(number: Int): List<UserState.Account> {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
) )
} }

View File

@ -221,6 +221,62 @@ class PolicyManagerTest {
assertTrue(policyManager.getActivePolicies(type = PolicyTypeJson.RESTRICT_ITEM_TYPES).any()) assertTrue(policyManager.getActivePolicies(type = PolicyTypeJson.RESTRICT_ITEM_TYPES).any())
} }
@Test
fun `getUserPolicies returns empty list if policies is null`() {
every {
authDiskSource.userState
} returns null
every {
authDiskSource.getPolicies(USER_ID)
} returns null
assertEquals(
emptyList<SyncResponseJson.Policy>(),
policyManager.getUserPolicies(
userId = USER_ID,
type = PolicyTypeJson.PERSONAL_OWNERSHIP,
),
)
}
@Test
fun `getUserPolicies returns active and applied Disabled personal vault export policies`() {
val userState: UserStateJson = mockk {
every { activeUserId } returns USER_ID
}
every { authDiskSource.userState } returns userState
every {
authDiskSource.getOrganizations(USER_ID)
} returns listOf(
createMockOrganization(
number = 3,
isEnabled = true,
shouldUsePolicies = true,
type = OrganizationType.USER,
),
)
val listOfPolicies = listOf(
createMockPolicy(
organizationId = "mockId-3",
isEnabled = true,
type = PolicyTypeJson.DISABLE_PERSONAL_VAULT_EXPORT,
),
)
every {
authDiskSource.getPolicies(USER_ID)
} returns listOfPolicies
assertEquals(
listOfPolicies,
policyManager.getUserPolicies(
userId = USER_ID,
type = PolicyTypeJson.DISABLE_PERSONAL_VAULT_EXPORT,
),
)
}
} }
private const val USER_ID = "userId" private const val USER_ID = "userId"

View File

@ -445,6 +445,7 @@ private val DEFAULT_USER_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.ACCOUNT_LOCK_SETUP, onboardingStatus = OnboardingStatus.ACCOUNT_LOCK_SETUP,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val CIPHER = mockk<Cipher>() private val CIPHER = mockk<Cipher>()

View File

@ -95,6 +95,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )
@ -247,6 +248,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
val userState = UserState( val userState = UserState(
activeUserId = "activeUserId", activeUserId = "activeUserId",
@ -304,6 +306,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
val userState = UserState( val userState = UserState(
activeUserId = "activeUserId", activeUserId = "activeUserId",
@ -365,6 +368,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
val userState = UserState( val userState = UserState(
activeUserId = "activeUserId", activeUserId = "activeUserId",
@ -528,6 +532,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
val userState = UserState( val userState = UserState(
@ -564,6 +569,7 @@ class LandingViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
val userState = UserState( val userState = UserState(

View File

@ -132,6 +132,7 @@ class LoginViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -304,6 +304,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -293,6 +293,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -269,6 +269,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )
@ -309,6 +310,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )
@ -1413,6 +1415,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -466,6 +466,7 @@ class RootNavScreenTest : BitwardenComposeTest() {
mockNavHostController.navigate( mockNavHostController.navigate(
route = VerifyPasswordRoute( route = VerifyPasswordRoute(
userId = "activeUserId", userId = "activeUserId",
hasOtherAccounts = false,
), ),
navOptions = expectedNavOptions, navOptions = expectedNavOptions,
) )

View File

@ -114,6 +114,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -151,6 +152,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -185,6 +187,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -229,6 +232,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -270,6 +274,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -311,6 +316,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -349,6 +355,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = true, hasPendingAccountAddition = true,
@ -402,6 +409,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -437,6 +445,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -477,6 +486,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -515,6 +525,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
firstTimeState = FirstTimeState(false), firstTimeState = FirstTimeState(false),
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
isExportable = true,
), ),
), ),
), ),
@ -558,6 +569,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -601,6 +613,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -651,6 +664,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -699,6 +713,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -744,6 +759,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -789,6 +805,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -924,6 +941,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -978,6 +996,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1040,6 +1059,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1082,6 +1102,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1119,6 +1140,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1154,6 +1176,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1192,6 +1215,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1230,6 +1254,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1268,6 +1293,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1306,6 +1332,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1344,6 +1371,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1382,6 +1410,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1420,6 +1449,7 @@ class RootNavViewModelTest : BaseViewModelTest() {
firstTimeState = FirstTimeState( firstTimeState = FirstTimeState(
showImportLoginsCard = true, showImportLoginsCard = true,
), ),
isExportable = true,
), ),
), ),
), ),
@ -1504,6 +1534,7 @@ private val MOCK_VAULT_UNLOCKED_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
firstTimeState = FirstTimeState(false), firstTimeState = FirstTimeState(false),
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
isExportable = true,
), ),
), ),
) )
@ -1529,6 +1560,7 @@ private val MOCK_VAULT_UNLOCKED_USER_MULTIPLE_ACCOUNTS_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
firstTimeState = FirstTimeState(false), firstTimeState = FirstTimeState(false),
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
isExportable = true,
), ),
UserState.Account( UserState.Account(
@ -1549,6 +1581,7 @@ private val MOCK_VAULT_UNLOCKED_USER_MULTIPLE_ACCOUNTS_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
firstTimeState = FirstTimeState(false), firstTimeState = FirstTimeState(false),
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
isExportable = true,
), ),
), ),
) )

View File

@ -1863,6 +1863,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -981,6 +981,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -248,6 +248,7 @@ private val DEFAULT_USER_STATE: UserState = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -496,6 +496,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
UserState.Account( UserState.Account(
userId = USER_ID_2, userId = USER_ID_2,
@ -515,6 +516,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -855,6 +855,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -2635,6 +2635,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -706,6 +706,7 @@ private val DEFAULT_USER_ACCOUNT_STATE = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -1075,6 +1075,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -4782,6 +4782,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
hasPendingAccountAddition = false, hasPendingAccountAddition = false,

View File

@ -629,6 +629,7 @@ class CipherViewExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
} }

View File

@ -659,6 +659,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -53,7 +53,7 @@ class SelectAccountScreenTest : BitwardenComposeTest() {
credentialExchangeRequestValidator = credentialExchangeRequestValidator, credentialExchangeRequestValidator = credentialExchangeRequestValidator,
) { ) {
SelectAccountScreen( SelectAccountScreen(
onAccountSelected = { onAccountSelectedCalled = true }, onAccountSelected = { _, _ -> onAccountSelectedCalled = true },
viewModel = viewModel, viewModel = viewModel,
) )
} }
@ -126,6 +126,7 @@ class SelectAccountScreenTest : BitwardenComposeTest() {
mockEventFlow.emit( mockEventFlow.emit(
SelectAccountEvent.NavigateToPasswordVerification( SelectAccountEvent.NavigateToPasswordVerification(
userId = ACTIVE_ACCOUNT_SUMMARY.userId, userId = ACTIVE_ACCOUNT_SUMMARY.userId,
hasOtherAccounts = true,
), ),
) )

View File

@ -127,7 +127,6 @@ class SelectAccountViewModelTest : BaseViewModelTest() {
verify(Ordering.ORDERED) { verify(Ordering.ORDERED) {
authRepository.userStateFlow authRepository.userStateFlow
policyManager.getActivePoliciesFlow(PolicyTypeJson.RESTRICT_ITEM_TYPES) policyManager.getActivePoliciesFlow(PolicyTypeJson.RESTRICT_ITEM_TYPES)
policyManager.getActivePoliciesFlow(PolicyTypeJson.PERSONAL_OWNERSHIP)
} }
} }
@ -146,7 +145,6 @@ class SelectAccountViewModelTest : BaseViewModelTest() {
SelectAccountAction.Internal.SelectionDataReceive( SelectAccountAction.Internal.SelectionDataReceive(
userState = DEFAULT_USER_STATE, userState = DEFAULT_USER_STATE,
itemRestrictedOrgs = emptyList(), itemRestrictedOrgs = emptyList(),
personalOwnershipOrgs = emptyList(),
), ),
) )
@ -166,6 +164,7 @@ class SelectAccountViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel() val viewModel = createViewModel()
val organizationId = "mockOrganizationId-1" val organizationId = "mockOrganizationId-1"
val accountInOrg = DEFAULT_ACCOUNT.copy( val accountInOrg = DEFAULT_ACCOUNT.copy(
isExportable = false,
organizations = listOf( organizations = listOf(
Organization( Organization(
id = organizationId, id = organizationId,
@ -183,13 +182,6 @@ class SelectAccountViewModelTest : BaseViewModelTest() {
SelectAccountAction.Internal.SelectionDataReceive( SelectAccountAction.Internal.SelectionDataReceive(
userState = DEFAULT_USER_STATE.copy(accounts = listOf(accountInOrg)), userState = DEFAULT_USER_STATE.copy(accounts = listOf(accountInOrg)),
itemRestrictedOrgs = emptyList(), itemRestrictedOrgs = emptyList(),
personalOwnershipOrgs = listOf(
createMockPolicy(
number = 1,
id = organizationId,
isEnabled = true,
),
),
), ),
) )
assertEquals( assertEquals(
@ -237,7 +229,6 @@ class SelectAccountViewModelTest : BaseViewModelTest() {
isEnabled = true, isEnabled = true,
), ),
), ),
personalOwnershipOrgs = emptyList(),
), ),
) )
assertEquals( assertEquals(
@ -277,6 +268,7 @@ class SelectAccountViewModelTest : BaseViewModelTest() {
assertEquals( assertEquals(
SelectAccountEvent.NavigateToPasswordVerification( SelectAccountEvent.NavigateToPasswordVerification(
userId = DEFAULT_ACCOUNT.userId, userId = DEFAULT_ACCOUNT.userId,
hasOtherAccounts = true,
), ),
awaitItem(), awaitItem(),
) )
@ -308,6 +300,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -459,6 +459,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -281,6 +281,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -708,6 +708,7 @@ class VerifyPasswordViewModelTest : BaseViewModelTest() {
toVerifyPasswordArgs() toVerifyPasswordArgs()
} returns VerifyPasswordArgs( } returns VerifyPasswordArgs(
userId = DEFAULT_USER_ID, userId = DEFAULT_USER_ID,
hasOtherAccounts = true,
) )
}, },
) )
@ -746,6 +747,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
UserState.Account( UserState.Account(
@ -776,6 +778,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -2580,6 +2580,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE: UserState = UserState( private val DEFAULT_USER_STATE: UserState = UserState(

View File

@ -5878,6 +5878,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
private val DEFAULT_USER_STATE = UserState( private val DEFAULT_USER_STATE = UserState(

View File

@ -556,6 +556,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -138,6 +138,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState =
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )

View File

@ -309,6 +309,7 @@ class VaultViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = DEFAULT_FIRST_TIME_STATE, firstTimeState = DEFAULT_FIRST_TIME_STATE,
isExportable = true,
), ),
), ),
) )
@ -397,6 +398,7 @@ class VaultViewModelTest : BaseViewModelTest() {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = DEFAULT_FIRST_TIME_STATE, firstTimeState = DEFAULT_FIRST_TIME_STATE,
isExportable = true,
), ),
), ),
) )
@ -3166,6 +3168,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = DEFAULT_FIRST_TIME_STATE, firstTimeState = DEFAULT_FIRST_TIME_STATE,
isExportable = true,
), ),
UserState.Account( UserState.Account(
userId = "lockedUserId", userId = "lockedUserId",
@ -3185,6 +3188,7 @@ private val DEFAULT_USER_STATE = UserState(
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = DEFAULT_FIRST_TIME_STATE, firstTimeState = DEFAULT_FIRST_TIME_STATE,
isExportable = true,
), ),
), ),
) )

View File

@ -91,6 +91,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
UserState.Account( UserState.Account(
userId = "lockedUserId", userId = "lockedUserId",
@ -120,6 +121,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
UserState.Account( UserState.Account(
userId = "unlockedUserId", userId = "unlockedUserId",
@ -153,6 +155,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
UserState.Account( UserState.Account(
userId = "loggedOutUserId", userId = "loggedOutUserId",
@ -186,6 +189,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )
@ -234,6 +238,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
.toAccountSummary(isActive = true), .toAccountSummary(isActive = true),
) )
@ -280,6 +285,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
.toAccountSummary(isActive = false), .toAccountSummary(isActive = false),
) )
@ -330,6 +336,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
), ),
), ),
) )
@ -358,6 +365,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
.toVaultFilterData(isIndividualVaultDisabled = false), .toVaultFilterData(isIndividualVaultDisabled = false),
) )
@ -419,6 +427,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
.toVaultFilterData( .toVaultFilterData(
isIndividualVaultDisabled = false, isIndividualVaultDisabled = false,
@ -481,6 +490,7 @@ class UserStateExtensionsTest {
isUsingKeyConnector = false, isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE, onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true), firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
) )
.toVaultFilterData( .toVaultFilterData(
isIndividualVaultDisabled = true, isIndividualVaultDisabled = true,