[PM-26330] Correct owner data when individual vault is disabled (#5968)

This commit is contained in:
Patrick Honkonen
2025-10-02 11:56:50 -04:00
committed by GitHub
parent 1638a20bf0
commit 4e57f306d3
2 changed files with 146 additions and 35 deletions

View File

@@ -2,6 +2,7 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
import com.bitwarden.collections.CollectionType
import com.bitwarden.collections.CollectionView
import com.bitwarden.core.data.util.toFormattedDateTimeStyle
import com.bitwarden.ui.platform.resource.BitwardenString
@@ -139,12 +140,22 @@ fun VaultAddEditState.ViewState.appendFolderAndOwnerData(
.toSelectedOwnerId(cipherView = currentContentState.common.originalCipher)
?: collectionViewList
.firstOrNull { it.id == currentContentState.common.selectedCollectionId }
?.organizationId
?: collectionViewList
.getDefaultCollectionViewOrNull(
isIndividualVaultDisabled = isIndividualVaultDisabled,
)
?.organizationId,
availableOwners = activeAccount.toAvailableOwners(
collectionViewList = collectionViewList,
cipherView = currentContentState.common.originalCipher,
isIndividualVaultDisabled = isIndividualVaultDisabled,
selectedCollectionId = currentContentState.common.selectedCollectionId,
selectedCollectionId = currentContentState.common.selectedCollectionId
?: collectionViewList
.getDefaultCollectionViewOrNull(
isIndividualVaultDisabled = isIndividualVaultDisabled,
)
?.id,
),
isUnlockWithPasswordEnabled = activeAccount.hasMasterPassword,
hasOrganizations = activeAccount.organizations.isNotEmpty(),
@@ -153,6 +164,29 @@ fun VaultAddEditState.ViewState.appendFolderAndOwnerData(
} ?: this
}
/**
* Retrieves the default user collection from a list of [CollectionView]s, but only if the
* individual vault is disabled.
*
* This is used to pre-select the default collection for a new item when the user is part of an
* organization and the "Individual Vault" policy is enabled, which prevents them from creating
* items in their personal vault.
*
* @param isIndividualVaultDisabled A boolean indicating if the policy disabling the individual
* vault is active.
*
* @return The [CollectionView] corresponding to the default user collection if the individual vault
* is disabled, otherwise `null`.
*/
fun List<CollectionView>.getDefaultCollectionViewOrNull(
isIndividualVaultDisabled: Boolean,
): CollectionView? =
if (isIndividualVaultDisabled) {
firstOrNull { it.type == CollectionType.DEFAULT_USER_COLLECTION }
} else {
null
}
/**
* Validates a [CipherView] otherwise returning a [VaultAddEditState.ViewState.Error].
*/

View File

@@ -1,5 +1,6 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
import com.bitwarden.collections.CollectionType
import com.bitwarden.data.repository.model.Environment
import com.bitwarden.network.model.OrganizationType
import com.bitwarden.ui.platform.resource.BitwardenString
@@ -453,7 +454,18 @@ class CipherViewExtensionsTest {
@Test
fun `appendFolderAndOwnerData should append folder and owner data`() {
val viewState = createSecureNoteViewState(withFolderAndOwnerData = false)
val viewState = createSecureNoteViewState(
availableOwners = listOf(
USER_OWNER,
ORGANIZATION_OWNER,
),
availableFolders = listOf(
NO_FOLDER_ITEM,
MOCK_FOLDER_ITEM,
),
selectedFolderId = null,
selectedOwnerId = null,
)
val account = createAccount()
val folderView = listOf(createMockFolderView(number = 1))
val collectionList = listOf(createMockCollectionView(number = 1))
@@ -466,15 +478,74 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
)
val expected = createSecureNoteViewState(
availableOwners = listOf(
USER_OWNER,
ORGANIZATION_OWNER,
),
availableFolders = listOf(
NO_FOLDER_ITEM,
MOCK_FOLDER_ITEM,
),
selectedFolderId = MOCK_FOLDER_ITEM.id,
selectedOwnerId = ORGANIZATION_OWNER.id,
)
assertEquals(
createSecureNoteViewState(withFolderAndOwnerData = true),
expected,
result,
)
}
@Suppress("MaxLineLength")
@Test
fun `appendFolderAndOwnerData should append correct owner data if individual vault is disabled`() {
val mockCipherView = createMockCipherView(number = 1, organizationId = null)
val viewState = createSecureNoteViewState(
cipherView = mockCipherView,
availableOwners = listOf(USER_OWNER, ORGANIZATION_OWNER),
availableFolders = emptyList(),
selectedOwnerId = null,
selectedFolderId = null,
)
val account = createAccount()
val collectionList = listOf(
createMockCollectionView(
number = 1,
type = CollectionType.DEFAULT_USER_COLLECTION,
),
)
val result = viewState.appendFolderAndOwnerData(
folderViewList = emptyList(),
collectionViewList = collectionList,
activeAccount = account,
isIndividualVaultDisabled = true,
resourceManager = resourceManager,
)
val expected = createSecureNoteViewState(
cipherView = mockCipherView,
availableOwners = listOf(ORGANIZATION_OWNER),
availableFolders = listOf(NO_FOLDER_ITEM),
)
assertEquals(
expected,
result,
)
}
private fun createSecureNoteViewState(
cipherView: CipherView = createMockCipherView(number = 1),
withFolderAndOwnerData: Boolean,
availableOwners: List<VaultAddEditState.Owner> = listOf(
USER_OWNER,
ORGANIZATION_OWNER,
),
availableFolders: List<VaultAddEditState.Folder> = listOf(
NO_FOLDER_ITEM,
MOCK_FOLDER_ITEM,
),
selectedFolderId: String? = availableFolders.firstOrNull()?.id,
selectedOwnerId: String? = availableOwners.firstOrNull()?.id,
): VaultAddEditState.ViewState.Content =
VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
@@ -504,39 +575,21 @@ class CipherViewExtensionsTest {
availableOwners = emptyList(),
)
.let {
if (withFolderAndOwnerData) {
if (availableOwners.isNotEmpty()) {
it.copy(
selectedFolderId = "mockId-1",
selectedOwnerId = "mockOrganizationId-1",
availableFolders = listOf(
VaultAddEditState.Folder(
id = null,
name = "No Folder",
),
VaultAddEditState.Folder(
id = "mockId-1",
name = "mockName-1",
),
),
selectedOwnerId = selectedOwnerId,
hasOrganizations = true,
availableOwners = listOf(
VaultAddEditState.Owner(
id = null,
name = "activeEmail",
collections = emptyList(),
),
VaultAddEditState.Owner(
id = "mockOrganizationId-1",
name = "organizationName",
collections = listOf(
VaultCollection(
id = "mockId-1",
name = "mockName-1",
isSelected = true,
),
),
),
),
availableOwners = availableOwners,
)
} else {
it
}
}
.let {
if (availableFolders.isNotEmpty()) {
it.copy(
selectedFolderId = selectedFolderId,
availableFolders = availableFolders,
)
} else {
it
@@ -734,3 +787,27 @@ private val DEFAULT_SSH_KEY_CIPHER_VIEW: CipherView = DEFAULT_BASE_CIPHER_VIEW.c
)
private const val TEST_ID = "testID"
private val NO_FOLDER_ITEM = VaultAddEditState.Folder(
id = null,
name = "No Folder",
)
private val MOCK_FOLDER_ITEM = VaultAddEditState.Folder(
id = "mockId-1",
name = "mockName-1",
)
private val ORGANIZATION_OWNER = VaultAddEditState.Owner(
id = "mockOrganizationId-1",
name = "organizationName",
collections = listOf(
VaultCollection(
id = "mockId-1",
name = "mockName-1",
isSelected = true,
),
),
)
private val USER_OWNER = VaultAddEditState.Owner(
id = null,
name = "activeEmail",
collections = emptyList(),
)