[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
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.model.OnboardingStatus
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.userOrganizationsListFlow
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.vault.manager.VaultLockManager
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
@ -39,6 +42,7 @@ class UserStateManagerImpl(
private val authDiskSource: AuthDiskSource,
firstTimeActionManager: FirstTimeActionManager,
vaultLockManager: VaultLockManager,
private val policyManager: PolicyManager,
dispatcherManager: DispatcherManager,
) : UserStateManager {
private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined)
@ -110,6 +114,7 @@ class UserStateManagerImpl(
vaultUnlockTypeProvider = ::getVaultUnlockType,
isDeviceTrustedProvider = ::isDeviceTrusted,
firstTimeState = firstTimeState,
getUserPolicies = ::existingPolicies,
)
}
.filterNot {
@ -133,6 +138,7 @@ class UserStateManagerImpl(
vaultUnlockTypeProvider = ::getVaultUnlockType,
isDeviceTrustedProvider = ::isDeviceTrusted,
firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState,
getUserPolicies = ::existingPolicies,
),
)
@ -159,4 +165,12 @@ class UserStateManagerImpl(
.getPinProtectedUserKeyEnvelope(userId = userId)
?.let { VaultUnlockType.PIN }
?: 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,
firstTimeActionManager: FirstTimeActionManager,
vaultLockManager: VaultLockManager,
policyManager: PolicyManager,
dispatcherManager: DispatcherManager,
): UserStateManager = UserStateManagerImpl(
authDiskSource = authDiskSource,
firstTimeActionManager = firstTimeActionManager,
vaultLockManager = vaultLockManager,
policyManager = policyManager,
dispatcherManager = dispatcherManager,
)
}

View File

@ -75,6 +75,7 @@ data class UserState(
val isUsingKeyConnector: Boolean,
val onboardingStatus: OnboardingStatus,
val firstTimeState: FirstTimeState,
val isExportable: Boolean,
) {
/**
* 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.network.model.KdfTypeJson
import com.bitwarden.network.model.OrganizationType
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.UserDecryptionOptionsJson
import com.bitwarden.ui.platform.base.util.toHexColorRepresentation
@ -164,6 +165,7 @@ fun UserStateJson.toUserState(
isBiometricsEnabledProvider: (userId: String) -> Boolean,
vaultUnlockTypeProvider: (userId: String) -> VaultUnlockType,
isDeviceTrustedProvider: (userId: String) -> Boolean,
getUserPolicies: (userId: String, policy: PolicyTypeJson) -> List<SyncResponseJson.Policy>,
): UserState =
UserState(
activeUserId = this.activeUserId,
@ -203,6 +205,19 @@ fun UserStateJson.toUserState(
hasManageResetPasswordPermission.takeIf { trustedDevice != null }
val needsMasterPassword = decryptionOptions?.hasMasterPassword == false &&
(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(
userId = userId,
name = profile.name,
@ -231,6 +246,8 @@ fun UserStateJson.toUserState(
// using the app prior to the release of the onboarding flow.
onboardingStatus = onboardingStatus ?: OnboardingStatus.COMPLETE,
firstTimeState = firstTimeState,
isExportable = !hasPersonalOwnershipRestrictedOrg &&
!hasPersonalVaultExportRestrictedOrg,
)
},
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.
*/
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()
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.
*/

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
authDiskSource.userState = authDiskSource.userState?.toUpdatedUserStateJson(
syncResponse = syncResponse,
@ -323,12 +332,6 @@ class VaultSyncManagerImpl(
unlockVaultForOrganizationsIfNecessary(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(
userId = userId,
lastSyncTime = clock.instant(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1281,6 +1281,7 @@ private val DEFAULT_ACCOUNT = UserState.Account(
isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = DEFAULT_FIRST_TIME_STATE,
isExportable = true,
)
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.network.model.GetTokenResponseJson
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.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
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.toUserState
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.vault.manager.VaultLockManager
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
@ -48,12 +51,16 @@ class UserStateManagerTest {
every { isActiveUserUnlockingFlow } returns mutableIsActiveUserUnlockingFlow
}
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
private val policyManager: PolicyManager = mockk {
every { getUserPolicies(any(), any()) } returns emptyList()
}
private val userStateManager: UserStateManager = UserStateManagerImpl(
authDiskSource = fakeAuthDiskSource,
firstTimeActionManager = firstTimeActionManager,
vaultLockManager = vaultLockManager,
dispatcherManager = dispatcherManager,
policyManager = policyManager,
)
@BeforeEach
@ -87,6 +94,7 @@ class UserStateManagerTest {
vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD },
isDeviceTrustedProvider = { false },
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
),
awaitItem(),
)
@ -114,6 +122,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false },
onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
),
awaitItem(),
)
@ -132,6 +141,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false },
onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
),
awaitItem(),
)
@ -162,6 +172,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false },
onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
),
awaitItem(),
)
@ -181,6 +192,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false },
onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
)
val finalUserState = SINGLE_USER_STATE_2.toUserState(
vaultState = VAULT_UNLOCK_DATA,
@ -193,6 +205,7 @@ class UserStateManagerTest {
isDeviceTrustedProvider = { false },
onboardingStatus = null,
firstTimeState = FIRST_TIME_STATE,
getUserPolicies = { _, _ -> emptyList() },
)
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
@ -242,6 +255,41 @@ class UserStateManagerTest {
userStateManager.hasPendingAccountDeletion = false
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"

View File

@ -6,6 +6,7 @@ import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.KeyConnectorUserDecryptionOptionsJson
import com.bitwarden.network.model.MasterPasswordUnlockDataJson
import com.bitwarden.network.model.OrganizationType
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.bitwarden.network.model.UserDecryptionJson
@ -389,6 +390,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
),
@ -462,6 +464,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { false },
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -502,6 +505,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -571,6 +575,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { false },
onboardingStatus = OnboardingStatus.NOT_STARTED,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -617,6 +622,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -689,6 +695,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -735,6 +742,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.AUTOFILL_SETUP,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -807,6 +815,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = OnboardingStatus.AUTOFILL_SETUP,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -853,6 +862,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -925,6 +935,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -975,6 +986,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -1047,6 +1059,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -1076,6 +1089,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = false,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -1126,6 +1140,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -1155,6 +1170,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -1207,6 +1223,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -1254,6 +1271,7 @@ class UserStateJsonExtensionsTest {
isUsingKeyConnector = true,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -1328,6 +1346,7 @@ class UserStateJsonExtensionsTest {
isDeviceTrustedProvider = { true },
onboardingStatus = null,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
getUserPolicies = { _, _ -> emptyList() },
),
)
}
@ -1376,6 +1395,7 @@ class UserStateJsonExtensionsTest {
firstTimeState = FirstTimeState(
showImportLoginsCard = false,
),
isExportable = true,
),
),
hasPendingAccountAddition = true,
@ -1450,6 +1470,255 @@ class UserStateJsonExtensionsTest {
firstTimeState = FirstTimeState(
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,
onboardingStatus = OnboardingStatus.COMPLETE,
firstTimeState = FirstTimeState(showImportLoginsCard = true),
isExportable = true,
),
)
}

View File

@ -221,6 +221,62 @@ class PolicyManagerTest {
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"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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