diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModel.kt index b79f0d585..f54dff659 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModel.kt @@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull @@ -17,6 +18,7 @@ import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult @@ -61,6 +63,7 @@ class SearchViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val clock: Clock, private val clipboardManager: BitwardenClipboardManager, + private val policyManager: PolicyManager, private val autofillSelectionManager: AutofillSelectionManager, private val vaultRepo: VaultRepository, private val authRepo: AuthRepository, @@ -84,7 +87,11 @@ class SearchViewModel @Inject constructor( dialogState = null, vaultFilterData = when (searchType) { is SearchType.Sends -> null - is SearchType.Vault -> userState.activeAccount.toVaultFilterData() + is SearchType.Vault -> userState.activeAccount.toVaultFilterData( + isIndividualVaultDisabled = policyManager + .getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + .any(), + ) }, baseWebSendUrl = environmentRepo.environment.environmentUrlData.baseWebSendUrl, baseIconUrl = environmentRepo.environment.environmentUrlData.baseIconUrl, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditItemContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditItemContent.kt index 0474f6c73..cedb7a435 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditItemContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditItemContent.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.unit.dp import com.x8bit.bitwarden.R import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton +import com.x8bit.bitwarden.ui.platform.components.BitwardenPolicyWarningText import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCardTypeHandlers import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers @@ -58,6 +59,17 @@ fun VaultAddEditContent( modifier = modifier .semantics { testTagsAsResourceId = true }, ) { + item { + if (state.isIndividualVaultDisabled && isAddItemMode) { + BitwardenPolicyWarningText( + text = stringResource(R.string.personal_ownership_policy_in_effect), + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth(), + ) + } + } + item { BitwardenListHeaderText( label = stringResource(id = R.string.item_information), diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt index e0a103e57..15a867ef8 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt @@ -8,6 +8,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull @@ -17,6 +18,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratorResult +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult @@ -74,6 +76,7 @@ class VaultAddEditViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val authRepository: AuthRepository, private val clipboardManager: BitwardenClipboardManager, + private val policyManager: PolicyManager, private val vaultRepository: VaultRepository, private val generatorRepository: GeneratorRepository, private val settingsRepository: SettingsRepository, @@ -84,6 +87,9 @@ class VaultAddEditViewModel @Inject constructor( initialState = savedStateHandle[KEY_STATE] ?: run { val vaultAddEditType = VaultAddEditArgs(savedStateHandle).vaultAddEditType + val isIndividualVaultDisabled = policyManager + .getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + .any() // Check for autofill data to pre-populate val autofillSaveItem = specialCircumstanceManager @@ -104,11 +110,12 @@ class VaultAddEditViewModel @Inject constructor( } val defaultAddTypeContent = autofillSelectionData - ?.toDefaultAddTypeContent() + ?.toDefaultAddTypeContent(isIndividualVaultDisabled) ?: autofillSaveItem - ?.toDefaultAddTypeContent() + ?.toDefaultAddTypeContent(isIndividualVaultDisabled) ?: VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), + isIndividualVaultDisabled = isIndividualVaultDisabled, type = VaultAddEditState.ViewState.Content.ItemType.Login(), ) @@ -1140,8 +1147,11 @@ class VaultAddEditViewModel @Inject constructor( private fun VaultAddEditState.determineContentState( vaultData: VaultData, userData: UserState?, - ): VaultAddEditState = - copy( + ): VaultAddEditState { + val isIndividualVaultDisabled = policyManager + .getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + .any() + return copy( viewState = vaultData.cipherViewList .find { it.id == vaultAddEditType.vaultItemId } .validateCipherOrReturnErrorState( @@ -1152,6 +1162,7 @@ class VaultAddEditViewModel @Inject constructor( // or use the current state for Add (cipherView?.toViewState( isClone = isCloneMode, + isIndividualVaultDisabled = isIndividualVaultDisabled, resourceManager = resourceManager, ) ?: viewState) .appendFolderAndOwnerData( @@ -1159,10 +1170,12 @@ class VaultAddEditViewModel @Inject constructor( collectionViewList = vaultData.collectionViewList .filter { !it.readOnly }, activeAccount = currentAccount, + isIndividualVaultDisabled = isIndividualVaultDisabled, resourceManager = resourceManager, ) }, ) + } private fun handleVaultTotpCodeReceive(action: VaultAddEditAction.Internal.TotpCodeReceive) { when (action.totpResult) { @@ -1473,6 +1486,7 @@ data class VaultAddEditState( data class Content( val common: Common, val type: ItemType, + val isIndividualVaultDisabled: Boolean, val previousItemTypes: Map = emptyMap(), ) : ViewState() { @@ -1512,6 +1526,7 @@ data class VaultAddEditState( */ val selectedOwner: Owner? get() = availableOwners.find { it.id == selectedOwnerId } + ?: availableOwners.firstOrNull() /** * Helper to provide the currently selected folder. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensions.kt index 1815f6018..83debe1f7 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensions.kt @@ -11,11 +11,14 @@ import java.util.UUID * Returns pre-filled content that may be used for an "add" type * [VaultAddEditState.ViewState.Content]. */ -fun AutofillSaveItem.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content = +fun AutofillSaveItem.toDefaultAddTypeContent( + isIndividualVaultDisabled: Boolean, +): VaultAddEditState.ViewState.Content = when (this) { is AutofillSaveItem.Card -> { VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), + isIndividualVaultDisabled = isIndividualVaultDisabled, type = VaultAddEditState.ViewState.Content.ItemType.Card( number = this.number.orEmpty(), expirationMonth = VaultCardExpirationMonth @@ -35,6 +38,7 @@ fun AutofillSaveItem.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Cont common = VaultAddEditState.ViewState.Content.Common( name = simpleUri.orEmpty(), ), + isIndividualVaultDisabled = isIndividualVaultDisabled, type = VaultAddEditState.ViewState.Content.ItemType.Login( username = this.username.orEmpty(), password = this.password.orEmpty(), diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt index 2d8526bfb..e169772e2 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt @@ -10,7 +10,9 @@ import java.util.UUID * Returns pre-filled content that may be used for an "add" type * [VaultAddEditState.ViewState.Content]. */ -fun AutofillSelectionData.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content { +fun AutofillSelectionData.toDefaultAddTypeContent( + isIndividualVaultDisabled: Boolean, +): VaultAddEditState.ViewState.Content { val uri = this.uri val simpleUri = uri?.toHostOrPathOrNull() val defaultAddType = when (this.type) { @@ -34,6 +36,7 @@ fun AutofillSelectionData.toDefaultAddTypeContent(): VaultAddEditState.ViewState common = VaultAddEditState.ViewState.Content.Common( name = simpleUri.orEmpty(), ), + isIndividualVaultDisabled = isIndividualVaultDisabled, type = defaultAddType, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensions.kt index f4afff7b6..240ce1dca 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensions.kt @@ -30,6 +30,7 @@ import java.util.UUID */ fun CipherView.toViewState( isClone: Boolean, + isIndividualVaultDisabled: Boolean, resourceManager: ResourceManager, ): VaultAddEditState.ViewState = VaultAddEditState.ViewState.Content( @@ -86,6 +87,7 @@ fun CipherView.toViewState( availableOwners = emptyList(), customFieldData = this.fields.orEmpty().map { it.toCustomField() }, ), + isIndividualVaultDisabled = isIndividualVaultDisabled, ) /** @@ -95,6 +97,7 @@ fun VaultAddEditState.ViewState.appendFolderAndOwnerData( folderViewList: List, collectionViewList: List, activeAccount: UserState.Account, + isIndividualVaultDisabled: Boolean, resourceManager: ResourceManager, ): VaultAddEditState.ViewState { return (this as? VaultAddEditState.ViewState.Content)?.let { currentContentState -> @@ -111,6 +114,7 @@ fun VaultAddEditState.ViewState.appendFolderAndOwnerData( ), availableOwners = activeAccount.toAvailableOwners( collectionViewList = collectionViewList, + isIndividualVaultDisabled = isIndividualVaultDisabled, ), ), ) @@ -161,10 +165,17 @@ private fun UserState.Account.toSelectedOwnerId(cipherView: CipherView?): String private fun UserState.Account.toAvailableOwners( collectionViewList: List, + isIndividualVaultDisabled: Boolean, ): List = - listOf(VaultAddEditState.Owner(name = email, id = null, collections = emptyList())) - .plus( - organizations.map { + listOfNotNull( + VaultAddEditState.Owner( + name = email, + id = null, + collections = emptyList(), + ) + .takeUnless { isIndividualVaultDisabled }, + *organizations + .map { VaultAddEditState.Owner( name = it.name.orEmpty(), id = it.id, @@ -181,8 +192,9 @@ private fun UserState.Account.toAvailableOwners( ) }, ) - }, - ) + } + .toTypedArray(), + ) private fun FieldView.toCustomField() = when (this.type) { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index 35b40b58d..5a0bbd744 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -7,10 +7,12 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult import com.x8bit.bitwarden.data.vault.repository.model.VaultData @@ -52,6 +54,7 @@ class VaultViewModel @Inject constructor( private val authRepository: AuthRepository, private val clipboardManager: BitwardenClipboardManager, private val clock: Clock, + private val policyManager: PolicyManager, private val settingsRepository: SettingsRepository, private val vaultRepository: VaultRepository, ) : BaseViewModel( @@ -59,7 +62,11 @@ class VaultViewModel @Inject constructor( val userState = requireNotNull(authRepository.userStateFlow.value) val accountSummaries = userState.toAccountSummaries() val activeAccountSummary = userState.toActiveAccountSummary() - val vaultFilterData = userState.activeAccount.toVaultFilterData() + val vaultFilterData = userState.activeAccount.toVaultFilterData( + isIndividualVaultDisabled = policyManager + .getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + .any(), + ) val appBarTitle = vaultFilterData.toAppBarTitle() VaultState( appBarTitle = appBarTitle, @@ -414,7 +421,11 @@ class VaultViewModel @Inject constructor( // navigating. if (state.isSwitchingAccounts) return - val vaultFilterData = userState.activeAccount.toVaultFilterData() + val vaultFilterData = userState.activeAccount.toVaultFilterData( + isIndividualVaultDisabled = policyManager + .getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + .any(), + ) val appBarTitle = vaultFilterData.toAppBarTitle() mutableStateFlow.update { val accountSummaries = userState.toAccountSummaries() diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt index a94df55dc..2b6efe837 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensions.kt @@ -47,16 +47,18 @@ fun UserState.Account.toAccountSummary( * Converts the given [UserState.Account] to a [VaultFilterData] (if applicable). Filter data is * only relevant when the given account is associated with one or more organizations. */ -fun UserState.Account.toVaultFilterData(): VaultFilterData? = +fun UserState.Account.toVaultFilterData( + isIndividualVaultDisabled: Boolean, +): VaultFilterData? = this .organizations .takeIf { it.isNotEmpty() } ?.let { organizations -> VaultFilterData( selectedVaultFilterType = VaultFilterType.AllVaults, - vaultFilterTypes = listOf( + vaultFilterTypes = listOfNotNull( VaultFilterType.AllVaults, - VaultFilterType.MyVault, + VaultFilterType.MyVault.takeUnless { isIndividualVaultDisabled }, *organizations .sortedBy { it.name } .map { organization -> diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt index 5f65b8e9e..4c11cf988 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt @@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManagerImpl import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager @@ -21,6 +22,8 @@ import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.model.Environment +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson +import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView @@ -77,6 +80,11 @@ class SearchViewModelTest : BaseViewModelTest() { private val clipboardManager: BitwardenClipboardManager = mockk { every { setText(any()) } just runs } + private val policyManager: PolicyManager = mockk { + every { + getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } returns emptyList() + } private val mutableVaultDataStateFlow = MutableStateFlow>(DataState.Loading) private val vaultRepository: VaultRepository = mockk { @@ -124,6 +132,26 @@ class SearchViewModelTest : BaseViewModelTest() { assertEquals(state, viewModel.stateFlow.value) } + @Test + fun `initial state should be correct when user has PERSONAL_OWNERSHIP policy`() { + every { + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } returns listOf( + SyncResponseJson.Policy( + organizationId = "Test Org", + id = "testId", + type = PolicyTypeJson.PERSONAL_OWNERSHIP, + isEnabled = true, + data = null, + ), + ) + val viewModel = createViewModel() + assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) + verify { + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } + } + @Test fun `BackClick should emit NavigateBack`() = runTest { val viewModel = createViewModel() @@ -1232,6 +1260,7 @@ class SearchViewModelTest : BaseViewModelTest() { environmentRepo = environmentRepository, settingsRepo = settingsRepository, clipboardManager = clipboardManager, + policyManager = policyManager, specialCircumstanceManager = specialCircumstanceManager, autofillSelectionManager = autofillSelectionManager, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt index 03fa4faba..7699d04c4 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditScreenTest.kt @@ -305,6 +305,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Login(), + isIndividualVaultDisabled = false, ), ) } @@ -333,6 +334,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Login(), + isIndividualVaultDisabled = false, ), ) } @@ -371,6 +373,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Card(), + isIndividualVaultDisabled = false, ), ) } @@ -403,6 +406,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { type = VaultAddEditState.ViewState.Content.ItemType.Login( password = "p@ssw0rd", ), + isIndividualVaultDisabled = false, ), ) } @@ -440,6 +444,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { password = "p@ssw0rd", canViewPassword = false, ), + isIndividualVaultDisabled = false, ), ) } @@ -2490,6 +2495,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { originalCipher = createMockCipherView(1), ), type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = false, ), ) } @@ -2530,6 +2536,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { ), ), type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = false, ), ) } @@ -2558,6 +2565,48 @@ class VaultAddEditScreenTest : BaseComposeTest() { .assertIsDisplayed() } + @Test + fun `should display policy warning when personal vault is disabled for add item type`() { + mutableStateFlow.update { + it.copy( + vaultAddEditType = VaultAddEditType.AddItem, + viewState = VaultAddEditState.ViewState.Content( + common = VaultAddEditState.ViewState.Content.Common( + originalCipher = createMockCipherView(1), + ), + type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = true, + ), + ) + } + + composeTestRule + .onNodeWithTextAfterScroll( + text = "An organization policy is affecting your ownership options.", + ) + .assertIsDisplayed() + } + + @Test + fun `should not display policy warning when personal vault is disabled for edit item type`() { + mutableStateFlow.update { + it.copy( + vaultAddEditType = VaultAddEditType.EditItem("mockId-1"), + viewState = VaultAddEditState.ViewState.Content( + common = VaultAddEditState.ViewState.Content.Common( + originalCipher = createMockCipherView(1), + ), + type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = true, + ), + ) + } + + composeTestRule + .onNodeWithText(text = "An organization policy is affecting your ownership options.") + .assertDoesNotExist() + } + @Test fun `Delete dialog ok click should send ConfirmDeleteClick`() { mutableStateFlow.update { @@ -2568,6 +2617,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { originalCipher = createMockCipherView(1), ), type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = false, ), ) } @@ -2615,6 +2665,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { originalCipher = createMockCipherView(1), ), type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = false, ), ) } @@ -2763,6 +2814,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Login(), + isIndividualVaultDisabled = false, ), dialog = VaultAddEditState.DialogState.Generic(message = "test".asText()), vaultAddEditType = VaultAddEditType.AddItem, @@ -2773,6 +2825,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Login(), + isIndividualVaultDisabled = false, ), dialog = null, ) @@ -2782,6 +2835,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Identity(), + isIndividualVaultDisabled = false, ), dialog = null, ) @@ -2791,6 +2845,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.Card(), + isIndividualVaultDisabled = false, ), dialog = null, ) @@ -2810,6 +2865,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { ), ), type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = false, ), dialog = null, vaultAddEditType = VaultAddEditType.AddItem, @@ -2820,6 +2876,7 @@ class VaultAddEditScreenTest : BaseComposeTest() { viewState = VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, + isIndividualVaultDisabled = false, ), dialog = null, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt index 30e8f806f..cad26fc84 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager @@ -26,6 +27,8 @@ import com.x8bit.bitwarden.data.platform.repository.model.Environment import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository import com.x8bit.bitwarden.data.tools.generator.repository.util.FakeGeneratorRepository +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson +import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult @@ -95,6 +98,11 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { every { getString(R.string.folder_none) } returns "No Folder" } private val clipboardManager: BitwardenClipboardManager = mockk() + private val policyManager: PolicyManager = mockk { + every { + getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } returns emptyList() + } private val vaultRepository: VaultRepository = mockk { every { vaultDataStateFlow } returns mutableVaultDataFlow every { totpCodeFlow } returns totpTestCodeFlow @@ -134,6 +142,9 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { awaitItem(), ) } + verify { + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } } @Test @@ -155,6 +166,57 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { } } + @Test + fun `initial add state should be correct with individual vault disabled`() = runTest { + every { + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } returns listOf( + SyncResponseJson.Policy( + organizationId = "Test Org", + id = "testId", + type = PolicyTypeJson.PERSONAL_OWNERSHIP, + isEnabled = true, + data = null, + ), + ) + val vaultAddEditType = VaultAddEditType.AddItem + mutableVaultDataFlow.value = DataState.Loaded( + data = createVaultData(), + ) + val viewModel = createAddVaultItemViewModel( + savedStateHandle = createSavedStateHandleWithState( + state = null, + vaultAddEditType = vaultAddEditType, + ), + ) + assertEquals( + VaultAddEditState( + vaultAddEditType = vaultAddEditType, + viewState = VaultAddEditState.ViewState.Content( + common = createCommonContentViewState( + availableOwners = listOf( + VaultAddEditState.Owner( + id = "organizationId", + name = "organizationName", + collections = emptyList(), + ), + ), + ), + isIndividualVaultDisabled = true, + type = VaultAddEditState.ViewState.Content.ItemType.Login(), + ), + dialog = null, + ), + viewModel.stateFlow.value, + ) + verify(exactly = 1) { + vaultRepository.vaultDataStateFlow + } + verify { + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } + } + @Test fun `initial add state should be correct when autofill selection`() = runTest { val autofillSelectionData = AutofillSelectionData( @@ -165,7 +227,9 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { autofillSelectionData = autofillSelectionData, shouldFinishWhenComplete = true, ) - val autofillContentState = autofillSelectionData.toDefaultAddTypeContent() + val autofillContentState = autofillSelectionData.toDefaultAddTypeContent( + isIndividualVaultDisabled = false, + ) val vaultAddEditType = VaultAddEditType.AddItem val initState = createVaultAddItemState( vaultAddEditType = vaultAddEditType, @@ -197,7 +261,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { specialCircumstanceManager.specialCircumstance = SpecialCircumstance.AutofillSave( autofillSaveItem = autofillSaveItem, ) - val autofillContentState = autofillSaveItem.toDefaultAddTypeContent() + val autofillContentState = autofillSaveItem.toDefaultAddTypeContent(false) val vaultAddEditType = VaultAddEditType.AddItem val initState = createVaultAddItemState( vaultAddEditType = vaultAddEditType, @@ -685,6 +749,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { every { cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = false, resourceManager = resourceManager, ) } returns stateWithName.viewState @@ -713,6 +778,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { coVerify(exactly = 1) { cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = false, resourceManager = resourceManager, ) vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any()) @@ -744,6 +810,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { every { cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = false, resourceManager = resourceManager, ) } returns stateWithName.viewState @@ -801,7 +868,11 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val errorMessage = "You do not have permission to edit this." every { - cipherView.toViewState(isClone = false, resourceManager = resourceManager) + cipherView.toViewState( + isClone = false, + isIndividualVaultDisabled = false, + resourceManager = resourceManager, + ) } returns stateWithName.viewState coEvery { vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any()) @@ -907,6 +978,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), previousItemTypes = mapOf( VaultAddEditState.ItemTypeOption.LOGIN @@ -936,6 +1008,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Card(), previousItemTypes = mapOf( VaultAddEditState.ItemTypeOption.LOGIN @@ -965,6 +1038,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Identity(), previousItemTypes = mapOf( VaultAddEditState.ItemTypeOption.LOGIN @@ -994,6 +1068,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, previousItemTypes = mapOf( VaultAddEditState.ItemTypeOption.LOGIN @@ -1074,6 +1149,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val loginState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState( password = password, ), @@ -1209,6 +1285,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState( totpCode = null, ), @@ -1233,6 +1310,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState( totpCode = "TestKey", ), @@ -1274,6 +1352,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState( uri = listOf(UriItem("testID", "Test", null)), ), @@ -1306,6 +1385,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { val expectedState = loginInitialState.copy( viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState(), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState( uri = listOf(), ), @@ -1772,6 +1852,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { vaultRepository = vaultRepository, generatorRepository = generatorRepository, specialCircumstanceManager = specialCircumstanceManager, + policyManager = policyManager, resourceManager = resourceManager, authRepository = authRepository, settingsRepository = settingsRepository, @@ -1789,6 +1870,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { common = createCommonContentViewState( name = "newName", ), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), ), ) @@ -1811,6 +1893,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState() .copy(selectedFolderId = "mockId-1"), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), ), ) @@ -1829,6 +1912,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { common = createCommonContentViewState( favorite = true, ), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), ), ) @@ -1850,6 +1934,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { common = createCommonContentViewState( masterPasswordReprompt = true, ), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), ), ) @@ -1869,6 +1954,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { common = createCommonContentViewState( notes = "newNotes", ), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), ), ) @@ -1892,6 +1978,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { viewState = VaultAddEditState.ViewState.Content( common = createCommonContentViewState() .copy(selectedOwnerId = "mockId-1"), + isIndividualVaultDisabled = false, type = createLoginTypeContentViewState(), ), ) @@ -2217,6 +2304,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { private fun createVaultAddItemState( vaultAddEditType: VaultAddEditType = VaultAddEditType.AddItem, commonContentViewState: VaultAddEditState.ViewState.Content.Common = createCommonContentViewState(), + isIndividualVaultDisabled: Boolean = false, typeContentViewState: VaultAddEditState.ViewState.Content.ItemType = createLoginTypeContentViewState(), dialogState: VaultAddEditState.DialogState? = null, ): VaultAddEditState = @@ -2224,6 +2312,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { vaultAddEditType = vaultAddEditType, viewState = VaultAddEditState.ViewState.Content( common = commonContentViewState, + isIndividualVaultDisabled = isIndividualVaultDisabled, type = typeContentViewState, ), dialog = dialogState, @@ -2313,6 +2402,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { vaultRepository = vaultRepo, generatorRepository = generatorRepo, specialCircumstanceManager = specialCircumstanceManager, + policyManager = policyManager, resourceManager = bitwardenResourceManager, authRepository = authRepository, settingsRepository = settingsRepository, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensionsTest.kt index 0b7dc9d12..202128ec8 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSaveItemExtensionsTest.kt @@ -28,6 +28,7 @@ class AutofillSaveItemExtensionsTest { assertEquals( VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Card( number = "number", expirationMonth = VaultCardExpirationMonth.JANUARY, @@ -41,7 +42,7 @@ class AutofillSaveItemExtensionsTest { expirationYear = "2024", securityCode = "securityCode", ) - .toDefaultAddTypeContent(), + .toDefaultAddTypeContent(isIndividualVaultDisabled = false), ) } @@ -53,6 +54,7 @@ class AutofillSaveItemExtensionsTest { common = VaultAddEditState.ViewState.Content.Common( name = "www.test.com", ), + isIndividualVaultDisabled = true, type = VaultAddEditState.ViewState.Content.ItemType.Login( username = "username", password = "password", @@ -70,7 +72,7 @@ class AutofillSaveItemExtensionsTest { password = "password", uri = "https://www.test.com", ) - .toDefaultAddTypeContent(), + .toDefaultAddTypeContent(isIndividualVaultDisabled = true), ) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt index 6ed0754de..baaec7b85 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt @@ -27,13 +27,14 @@ class AutofillSelectionDataExtensionsTest { assertEquals( VaultAddEditState.ViewState.Content( common = VaultAddEditState.ViewState.Content.Common(), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Card(), ), AutofillSelectionData( type = AutofillSelectionData.Type.CARD, uri = null, ) - .toDefaultAddTypeContent(), + .toDefaultAddTypeContent(isIndividualVaultDisabled = false), ) } @@ -45,6 +46,7 @@ class AutofillSelectionDataExtensionsTest { common = VaultAddEditState.ViewState.Content.Common( name = "www.test.com", ), + isIndividualVaultDisabled = true, type = VaultAddEditState.ViewState.Content.ItemType.Login( uriList = listOf( UriItem( @@ -59,7 +61,7 @@ class AutofillSelectionDataExtensionsTest { type = AutofillSelectionData.Type.LOGIN, uri = "https://www.test.com", ) - .toDefaultAddTypeContent(), + .toDefaultAddTypeContent(isIndividualVaultDisabled = true), ) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt index 3eb9e6718..5fab1bd0c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt @@ -52,6 +52,7 @@ class CipherViewExtensionsTest { val result = cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = false, resourceManager = resourceManager, ) @@ -76,6 +77,7 @@ class CipherViewExtensionsTest { availableFolders = emptyList(), availableOwners = emptyList(), ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Card( cardHolderName = "Bit Warden", number = "4012888888881881", @@ -94,6 +96,7 @@ class CipherViewExtensionsTest { val result = cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = true, resourceManager = resourceManager, ) @@ -118,6 +121,7 @@ class CipherViewExtensionsTest { availableFolders = emptyList(), availableOwners = emptyList(), ), + isIndividualVaultDisabled = true, type = VaultAddEditState.ViewState.Content.ItemType.Identity( firstName = "John", middleName = "Richard", @@ -141,6 +145,7 @@ class CipherViewExtensionsTest { val result = cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = false, resourceManager = resourceManager, ) @@ -165,6 +170,7 @@ class CipherViewExtensionsTest { ), ), ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Login( username = "username", password = "password", @@ -183,6 +189,7 @@ class CipherViewExtensionsTest { val result = cipherView.toViewState( isClone = false, + isIndividualVaultDisabled = true, resourceManager = resourceManager, ) @@ -202,6 +209,7 @@ class CipherViewExtensionsTest { availableFolders = emptyList(), availableOwners = emptyList(), ), + isIndividualVaultDisabled = true, type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, ), result, @@ -214,6 +222,7 @@ class CipherViewExtensionsTest { val result = cipherView.toViewState( isClone = true, + isIndividualVaultDisabled = false, resourceManager = resourceManager, ) @@ -233,6 +242,7 @@ class CipherViewExtensionsTest { availableFolders = emptyList(), availableOwners = emptyList(), ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, ), result, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index d2e3ac5d0..cd33780ba 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -6,11 +6,14 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState +import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.model.Environment import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson +import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView @@ -52,6 +55,11 @@ class VaultViewModelTest : BaseViewModelTest() { private val clipboardManager: BitwardenClipboardManager = mockk { every { setText(any()) } just runs } + private val policyManager: PolicyManager = mockk { + every { + getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } returns emptyList() + } private val mutablePullToRefreshEnabledFlow = MutableStateFlow(false) private val mutableIsIconLoadingDisabledFlow = MutableStateFlow(false) @@ -93,7 +101,10 @@ class VaultViewModelTest : BaseViewModelTest() { fun `initial state should be correct and should trigger a syncIfNecessary call`() { val viewModel = createViewModel() assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) - verify { vaultRepository.syncIfNecessary() } + verify { + vaultRepository.syncIfNecessary() + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } } @Test @@ -143,7 +154,7 @@ class VaultViewModelTest : BaseViewModelTest() { @Suppress("MaxLineLength") @Test - fun `UserState updates with a non-null value when not switching accounts should update the account information in the state`() { + fun `UserState updates with a non-null value when not switching accounts should update the account information in the state when personal ownership enabled`() { val viewModel = createViewModel() assertEquals( DEFAULT_STATE, @@ -207,6 +218,82 @@ class VaultViewModelTest : BaseViewModelTest() { ) } + @Suppress("MaxLineLength") + @Test + fun `UserState updates with a non-null value when not switching accounts should update the account information in the state when personal ownership disabled`() { + every { + policyManager.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP) + } returns listOf( + SyncResponseJson.Policy( + organizationId = "Test Organization", + id = "testId", + type = PolicyTypeJson.PERSONAL_OWNERSHIP, + isEnabled = true, + data = null, + ), + ) + val viewModel = createViewModel() + assertEquals( + DEFAULT_STATE, + viewModel.stateFlow.value, + ) + + mutableUserStateFlow.value = + DEFAULT_USER_STATE.copy( + accounts = listOf( + UserState.Account( + userId = "activeUserId", + name = "Other User", + email = "active@bitwarden.com", + avatarColorHex = "#00aaaa", + environment = Environment.Us, + isPremium = true, + isLoggedIn = true, + isVaultUnlocked = true, + needsPasswordReset = false, + isBiometricsEnabled = false, + organizations = listOf( + Organization( + id = "organizationId", + name = "Test Organization", + ), + ), + ), + ), + ) + + assertEquals( + DEFAULT_STATE.copy( + appBarTitle = R.string.vaults.asText(), + avatarColorString = "#00aaaa", + initials = "OU", + accountSummaries = listOf( + AccountSummary( + userId = "activeUserId", + name = "Other User", + email = "active@bitwarden.com", + avatarColorHex = "#00aaaa", + environmentLabel = "bitwarden.com", + isActive = true, + isLoggedIn = true, + isVaultUnlocked = true, + ), + ), + vaultFilterData = VaultFilterData( + selectedVaultFilterType = VaultFilterType.AllVaults, + vaultFilterTypes = listOf( + VaultFilterType.AllVaults, + VaultFilterType.OrganizationVault( + organizationId = "organizationId", + organizationName = "Test Organization", + ), + ), + ), + ), + viewModel.stateFlow.value, + ) + } + @Test fun `on LockAccountClick should call lockVault for the given account`() { val accountUserId = "userId" @@ -1245,6 +1332,7 @@ class VaultViewModelTest : BaseViewModelTest() { VaultViewModel( authRepository = authRepository, clipboardManager = clipboardManager, + policyManager = policyManager, clock = clock, settingsRepository = settingsRepository, vaultRepository = vaultRepository, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt index 362829aec..bd928a838 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt @@ -273,13 +273,13 @@ class UserStateExtensionsTest { isBiometricsEnabled = false, organizations = emptyList(), ) - .toVaultFilterData(), + .toVaultFilterData(isIndividualVaultDisabled = false), ) } @Suppress("MaxLineLength") @Test - fun `toVaultFilterData for an account with organizations should return data with the available types in the correct order`() { + fun `toVaultFilterData for an account with organizations and individual vault enabled should return data with the available types in the correct order`() { assertEquals( VaultFilterData( selectedVaultFilterType = VaultFilterType.AllVaults, @@ -318,7 +318,55 @@ class UserStateExtensionsTest { ), ), ) - .toVaultFilterData(), + .toVaultFilterData( + isIndividualVaultDisabled = false, + ), + ) + } + + @Suppress("MaxLineLength") + @Test + fun `toVaultFilterData for an account with organizations and individual vault disabled should return data with the available types in the correct order`() { + assertEquals( + VaultFilterData( + selectedVaultFilterType = VaultFilterType.AllVaults, + vaultFilterTypes = listOf( + VaultFilterType.AllVaults, + VaultFilterType.OrganizationVault( + organizationId = "organizationId-A", + organizationName = "Organization A", + ), + VaultFilterType.OrganizationVault( + organizationId = "organizationId-B", + organizationName = "Organization B", + ), + ), + ), + UserState.Account( + userId = "activeUserId", + name = "name", + email = "email", + avatarColorHex = "avatarColorHex", + environment = Environment.Us, + isPremium = true, + isLoggedIn = true, + isVaultUnlocked = true, + needsPasswordReset = false, + isBiometricsEnabled = false, + organizations = listOf( + Organization( + id = "organizationId-B", + name = "Organization B", + ), + Organization( + id = "organizationId-A", + name = "Organization A", + ), + ), + ) + .toVaultFilterData( + isIndividualVaultDisabled = true, + ), ) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt index cc11bb91a..ef731bb7e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultAddItemStateExtensionsTest.kt @@ -47,6 +47,7 @@ class VaultAddItemStateExtensionsTest { notes = "mockNotes-1", selectedOwnerId = "mockOwnerId-1", ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Login( username = "mockUsername-1", password = "mockPassword-1", @@ -124,6 +125,7 @@ class VaultAddItemStateExtensionsTest { notes = "mockNotes-1", selectedOwnerId = "mockOwnerId-1", ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Login( username = "mockUsername-1", password = "mockPassword-1", @@ -212,6 +214,7 @@ class VaultAddItemStateExtensionsTest { VaultAddEditState.Custom.HiddenField("testId", "TestHidden", "TestHidden"), ), ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, ) @@ -281,6 +284,7 @@ class VaultAddItemStateExtensionsTest { selectedOwnerId = "mockOwnerId-1", customFieldData = emptyList(), ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes, ) @@ -314,6 +318,7 @@ class VaultAddItemStateExtensionsTest { notes = "mockNotes-1", selectedOwnerId = "mockOwnerId-1", ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Identity( selectedTitle = VaultIdentityTitle.MR, firstName = "mockFirstName", @@ -411,6 +416,7 @@ class VaultAddItemStateExtensionsTest { notes = "mockNotes-1", selectedOwnerId = "mockOwnerId-1", ), + isIndividualVaultDisabled = false, type = VaultAddEditState.ViewState.Content.ItemType.Identity( selectedTitle = VaultIdentityTitle.MR, firstName = "mockFirstName",