From f311a8f547e4bd785c352441a50c7bf9ca053021 Mon Sep 17 00:00:00 2001 From: Dave Severns Date: Tue, 26 Nov 2024 14:30:10 -0500 Subject: [PATCH 1/2] PM-14995 check for org ownership and premium status on items with TOTP function --- .../auth/repository/model/Organization.kt | 1 + .../util/SyncResponseJsonExtensions.kt | 1 + .../manager/AutofillTotpManagerImpl.kt | 10 +++- .../data/vault/manager/TotpCodeManagerImpl.kt | 1 + .../manager/model/VerificationCodeItem.kt | 1 + .../feature/search/SearchViewModel.kt | 6 ++ .../search/util/SearchTypeDataExtensions.kt | 6 +- .../vault/feature/item/VaultItemViewModel.kt | 22 ++++--- .../itemlisting/VaultItemListingViewModel.kt | 8 ++- .../util/VaultItemListingDataExtensions.kt | 6 +- .../ui/vault/feature/vault/VaultViewModel.kt | 8 +++ .../feature/vault/util/UserStateExtensions.kt | 8 +++ .../feature/vault/util/VaultDataExtensions.kt | 29 +++++++--- .../VerificationCodeViewModel.kt | 6 +- .../auth/repository/AuthRepositoryTest.kt | 3 + .../util/AuthDiskSourceExtensionsTest.kt | 6 ++ .../util/SyncResponseJsonExtensionsTest.kt | 3 + .../util/UserStateJsonExtensionsTest.kt | 16 +++++ .../manager/AutofillTotpManagerTest.kt | 35 +++++++++++ .../datasource/sdk/model/CipherViewUtil.kt | 6 +- .../data/vault/manager/TotpCodeManagerTest.kt | 7 ++- .../RemovePasswordViewModelTest.kt | 1 + .../feature/rootnav/RootNavViewModelTest.kt | 1 + .../feature/search/SearchScreenTest.kt | 1 + .../feature/search/SearchViewModelTest.kt | 6 ++ .../util/SearchTypeDataExtensionsTest.kt | 4 ++ .../addedit/VaultAddEditViewModelTest.kt | 1 + .../addedit/util/CipherViewExtensionsTest.kt | 1 + .../itemlisting/VaultItemListingScreenTest.kt | 1 + .../VaultItemListingViewModelTest.kt | 1 + .../VaultItemListingDataExtensionsTest.kt | 12 ++++ .../VaultMoveToOrganizationViewModelTest.kt | 3 + .../VaultMoveToOrganizationExtensionsTest.kt | 3 + .../ui/vault/feature/vault/VaultScreenTest.kt | 1 + .../vault/feature/vault/VaultViewModelTest.kt | 54 ++++++++++------- .../vault/util/UserStateExtensionsTest.kt | 58 +++++++++++++++++++ .../vault/util/VaultDataExtensionsTest.kt | 34 ++++++++--- .../VerificationCodeViewModelTest.kt | 1 + .../util/VerificationCodeDataUtil.kt | 1 + 39 files changed, 319 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt index ced0336bf0d..93b37e5cd61 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/Organization.kt @@ -18,4 +18,5 @@ data class Organization( val shouldManageResetPassword: Boolean, val shouldUseKeyConnector: Boolean, val role: OrganizationType, + val shouldUsersGetPremium: Boolean, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt index c124a136387..c3dc41f57f9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt @@ -22,6 +22,7 @@ fun SyncResponseJson.Profile.Organization.toOrganization(): Organization = shouldUseKeyConnector = this.shouldUseKeyConnector, role = this.type, shouldManageResetPassword = this.permissions.shouldManageResetPassword, + shouldUsersGetPremium = this.shouldUsersGetPremium, ) /** diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt index 5fff0e1f926..ef4af1babab 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt @@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardMan import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult +import com.x8bit.bitwarden.ui.vault.feature.vault.util.getOrganizationPremiumStatusMap import java.time.Clock /** @@ -24,8 +25,15 @@ class AutofillTotpManagerImpl( ) : AutofillTotpManager { override suspend fun tryCopyTotpToClipboard(cipherView: CipherView) { if (settingsRepository.isAutoCopyTotpDisabled) return + val organizationPremiumStatusMap = authRepository + .userStateFlow + .value + ?.activeAccount + ?.getOrganizationPremiumStatusMap() + ?: emptyMap() val isPremium = authRepository.userStateFlow.value?.activeAccount?.isPremium == true - if (!isPremium && !cipherView.organizationUseTotp) return + val premiumStatus = organizationPremiumStatusMap[cipherView.organizationId] ?: isPremium + if (!premiumStatus && !cipherView.organizationUseTotp) return val totpCode = cipherView.login?.totp ?: return val totpResult = vaultRepository.generateTotp( diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt index 3869c3d7d56..95cd4204fac 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerImpl.kt @@ -122,6 +122,7 @@ class TotpCodeManagerImpl( CipherRepromptType.NONE -> false }, orgUsesTotp = cipher.organizationUseTotp, + orgId = cipher.organizationId, ) } .onFailure { diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt index fadc0f357b7..7bfab7a3fc0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/model/VerificationCodeItem.kt @@ -29,4 +29,5 @@ data class VerificationCodeItem( val username: String?, val hasPasswordReprompt: Boolean, val orgUsesTotp: Boolean, + val orgId: String?, ) 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 f498b83721b..6997b9e41e3 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 @@ -46,6 +46,7 @@ import com.x8bit.bitwarden.ui.platform.feature.search.util.updateWithAdditionalD import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType +import com.x8bit.bitwarden.ui.vault.feature.vault.util.getOrganizationPremiumStatusMap import com.x8bit.bitwarden.ui.vault.feature.vault.util.toFilteredList import com.x8bit.bitwarden.ui.vault.feature.vault.util.toVaultFilterData import com.x8bit.bitwarden.ui.vault.model.TotpData @@ -107,6 +108,9 @@ class SearchViewModel @Inject constructor( totpData = specialCircumstance?.toTotpDataOrNull(), hasMasterPassword = userState.activeAccount.hasMasterPassword, isPremium = userState.activeAccount.isPremium, + organizationPremiumStatusMap = userState + .activeAccount + .getOrganizationPremiumStatusMap(), ) }, ) { @@ -687,6 +691,7 @@ class SearchViewModel @Inject constructor( isAutofill = state.isAutofill, isTotp = state.isTotp, isPremiumUser = state.isPremium, + organizationPremiumStatusMap = state.organizationPremiumStatusMap, ) } @@ -733,6 +738,7 @@ data class SearchState( val totpData: TotpData?, val hasMasterPassword: Boolean, val isPremium: Boolean, + val organizationPremiumStatusMap: Map, ) : Parcelable { /** diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensions.kt index 650dfd7070f..c9b7ca6d58e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensions.kt @@ -152,6 +152,7 @@ fun List.toViewState( isAutofill: Boolean, isTotp: Boolean, isPremiumUser: Boolean, + organizationPremiumStatusMap: Map, ): SearchState.ViewState = when { searchTerm.isEmpty() -> SearchState.ViewState.Empty(message = null) @@ -164,6 +165,7 @@ fun List.toViewState( isAutofill = isAutofill, isTotp = isTotp, isPremiumUser = isPremiumUser, + organizationPremiumStatusMap = organizationPremiumStatusMap, ) .sortAlphabetically(), ) @@ -184,15 +186,17 @@ private fun List.toDisplayItemList( isAutofill: Boolean, isTotp: Boolean, isPremiumUser: Boolean, + organizationPremiumStatusMap: Map, ): List = this.map { + val premiumStatus = organizationPremiumStatusMap[it.organizationId] ?: isPremiumUser it.toDisplayItem( baseIconUrl = baseIconUrl, hasMasterPassword = hasMasterPassword, isIconLoadingDisabled = isIconLoadingDisabled, isAutofill = isAutofill, isTotp = isTotp, - isPremiumUser = isPremiumUser, + isPremiumUser = premiumStatus, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt index 349344c07fc..c6ba8363f38 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt @@ -8,6 +8,7 @@ import com.bitwarden.vault.CipherView 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.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager @@ -1028,14 +1029,19 @@ class VaultItemViewModel @Inject constructor( ): VaultItemState.ViewState = this .data ?.cipher - ?.toViewState( - previousState = state.viewState.asContentOrNull(), - isPremiumUser = account.isPremium, - hasMasterPassword = account.hasMasterPassword, - totpCodeItemData = this.data?.totpCodeItemData, - canDelete = this.data?.canDelete == true, - canAssignToCollections = this.data?.canAssociateToCollections == true, - ) + ?.let { cipher -> + val ownerOrg: Organization? = account.organizations.find { + cipher.organizationId == it.id + } + cipher.toViewState( + previousState = state.viewState.asContentOrNull(), + isPremiumUser = ownerOrg?.shouldUsersGetPremium ?: account.isPremium, + hasMasterPassword = account.hasMasterPassword, + totpCodeItemData = this.data?.totpCodeItemData, + canDelete = this.data?.canDelete == true, + canAssignToCollections = this.data?.canAssociateToCollections == true, + ) + } ?: VaultItemState.ViewState.Error(message = errorText) private fun handleValidatePasswordReceive( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index 900511351b2..699917f3cd5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -13,9 +13,9 @@ import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilitySelectionManager import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager +import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CreateCredentialRequest import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionRequest import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialAssertionResult -import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CreateCredentialRequest import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2GetCredentialsRequest import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2GetCredentialsResult import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResult @@ -70,6 +70,7 @@ import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.toVaultItemCipherTy import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.toViewState import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.updateWithAdditionalDataIfNecessary import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType +import com.x8bit.bitwarden.ui.vault.feature.vault.util.getOrganizationPremiumStatusMap import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummaries import com.x8bit.bitwarden.ui.vault.feature.vault.util.toActiveAccountSummary import com.x8bit.bitwarden.ui.vault.feature.vault.util.toFilteredList @@ -138,6 +139,9 @@ class VaultItemListingViewModel @Inject constructor( fido2GetCredentialsRequest = specialCircumstance?.toFido2GetCredentialsRequestOrNull(), isPremium = userState.activeAccount.isPremium, isRefreshing = false, + organizationPremiumStatusMap = userState + .activeAccount + .getOrganizationPremiumStatusMap(), ) }, ) { @@ -1580,6 +1584,7 @@ class VaultItemListingViewModel @Inject constructor( .fido2CredentialAutofillViewList, totpData = state.totpData, isPremiumUser = state.isPremium, + organizationPremiumStatusMap = state.organizationPremiumStatusMap, ) } @@ -1745,6 +1750,7 @@ data class VaultItemListingState( val hasMasterPassword: Boolean, val isPremium: Boolean, val isRefreshing: Boolean, + val organizationPremiumStatusMap: Map, ) { /** * Whether or not the add FAB should be shown. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt index 6bf51edd328..53ebfcb3a27 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt @@ -111,6 +111,7 @@ fun VaultData.toViewState( fido2CredentialAutofillViews: List?, totpData: TotpData?, isPremiumUser: Boolean, + organizationPremiumStatusMap: Map, ): VaultItemListingState.ViewState { val filteredCipherViewList = cipherViewList .filter { cipherView -> @@ -142,6 +143,7 @@ fun VaultData.toViewState( fido2CredentialAutofillViews = fido2CredentialAutofillViews, isPremiumUser = isPremiumUser, isTotp = totpData != null, + organizationPremiumStatusMap = organizationPremiumStatusMap, ), displayFolderList = folderList.map { folderView -> VaultItemListingState.FolderDisplayItem( @@ -290,8 +292,10 @@ private fun List.toDisplayItemList( fido2CredentialAutofillViews: List?, isPremiumUser: Boolean, isTotp: Boolean, + organizationPremiumStatusMap: Map, ): List = this.map { + val premiumStatus = organizationPremiumStatusMap[it.organizationId] ?: isPremiumUser it.toDisplayItem( baseIconUrl = baseIconUrl, hasMasterPassword = hasMasterPassword, @@ -302,7 +306,7 @@ private fun List.toDisplayItemList( ?.firstOrNull { fido2CredentialAutofillView -> fido2CredentialAutofillView.cipherId == it.id }, - isPremiumUser = isPremiumUser, + isPremiumUser = premiumStatus, isTotp = isTotp, ) } 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 a3ed762b4e7..bbb77b8a4c8 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 @@ -37,6 +37,7 @@ import com.x8bit.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterData import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType +import com.x8bit.bitwarden.ui.vault.feature.vault.util.getOrganizationPremiumStatusMap import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials import com.x8bit.bitwarden.ui.vault.feature.vault.util.toAccountSummaries import com.x8bit.bitwarden.ui.vault.feature.vault.util.toActiveAccountSummary @@ -102,6 +103,9 @@ class VaultViewModel @Inject constructor( isRefreshing = false, showImportActionCard = false, showSshKeys = showSshKeys, + organizationPremiumStatusMap = userState + .activeAccount + .getOrganizationPremiumStatusMap(), ) }, ) { @@ -608,6 +612,7 @@ class VaultViewModel @Inject constructor( hasMasterPassword = state.hasMasterPassword, vaultFilterType = vaultFilterTypeOrDefault, showSshKeys = showSshKeys, + organizationPremiumStatusMap = state.organizationPremiumStatusMap, ), dialog = null, isRefreshing = false, @@ -644,6 +649,7 @@ class VaultViewModel @Inject constructor( hasMasterPassword = state.hasMasterPassword, vaultFilterType = vaultFilterTypeOrDefault, showSshKeys = state.showSshKeys, + organizationPremiumStatusMap = state.organizationPremiumStatusMap, ), ) } @@ -715,6 +721,7 @@ data class VaultState( val isRefreshing: Boolean, val showImportActionCard: Boolean, val showSshKeys: Boolean, + val organizationPremiumStatusMap: Map, ) : Parcelable { /** @@ -1334,6 +1341,7 @@ private fun MutableStateFlow.updateToErrorStateOrDialog( vaultFilterType = vaultFilterType, isIconLoadingDisabled = isIconLoadingDisabled, showSshKeys = it.showSshKeys, + organizationPremiumStatusMap = it.organizationPremiumStatusMap, ), dialog = VaultState.DialogState.Error( title = errorTitle, 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 2b6efe837a3..38cdcb4cf88 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 @@ -71,3 +71,11 @@ fun UserState.Account.toVaultFilterData( ), ) } + +/** + * Returns a map of organization IDs and if they provide a premium status to the user for + * items owned by that organization. + */ +fun UserState.Account.getOrganizationPremiumStatusMap(): Map { + return organizations.associate { it.id to it.shouldUsersGetPremium } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt index 4cc2f950259..259ae92b123 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt @@ -40,6 +40,7 @@ fun VaultData.toViewState( baseIconUrl: String, vaultFilterType: VaultFilterType, showSshKeys: Boolean, + organizationPremiumStatusMap: Map, ): VaultState.ViewState { val filteredCipherViewListWithDeletedItems = @@ -73,14 +74,24 @@ fun VaultData.toViewState( return if (filteredCipherViewListWithDeletedItems.isEmpty()) { VaultState.ViewState.NoItems } else { - val totpItems = filteredCipherViewList.filter { it.login?.totp != null } + val totpItemsGroupedByOwnership = filteredCipherViewList.groupBy { + !it.organizationId.isNullOrBlank() + } + val userOwnedTotpItems = totpItemsGroupedByOwnership[false] + ?.filter { + it.login?.totp != null && isPremium + } + ?: emptyList() + val organizationOwnedTotpItems = totpItemsGroupedByOwnership[true] + ?.filter { + it.login?.totp != null && + (organizationPremiumStatusMap[it.id] == true || it.organizationUseTotp) + } + ?: emptyList() VaultState.ViewState.Content( itemTypesCount = itemTypesCount, - totpItemsCount = if (isPremium) { - totpItems.count() - } else { - totpItems.count { it.organizationUseTotp } - }, + totpItemsCount = userOwnedTotpItems.count() + + organizationOwnedTotpItems.count(), loginItemsCount = filteredCipherViewList.count { it.type == CipherType.LOGIN }, cardItemsCount = filteredCipherViewList.count { it.type == CipherType.CARD }, identityItemsCount = filteredCipherViewList.count { it.type == CipherType.IDENTITY }, @@ -94,7 +105,8 @@ fun VaultData.toViewState( hasMasterPassword = hasMasterPassword, isIconLoadingDisabled = isIconLoadingDisabled, baseIconUrl = baseIconUrl, - isPremiumUser = isPremium, + isPremiumUser = organizationPremiumStatusMap[it.organizationId] + ?: isPremium, ) }, folderItems = filteredFolderViewList @@ -128,7 +140,8 @@ fun VaultData.toViewState( hasMasterPassword = hasMasterPassword, isIconLoadingDisabled = isIconLoadingDisabled, baseIconUrl = baseIconUrl, - isPremiumUser = isPremium, + isPremiumUser = organizationPremiumStatusMap[it.organizationId] + ?: isPremium, ) } .takeIf { it.size < NO_FOLDER_ITEM_THRESHOLD } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt index 261894f4d0b..900971f2fb5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt @@ -18,6 +18,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText import com.x8bit.bitwarden.ui.platform.base.util.concat import com.x8bit.bitwarden.ui.platform.components.model.IconData import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType +import com.x8bit.bitwarden.ui.vault.feature.vault.util.getOrganizationPremiumStatusMap import com.x8bit.bitwarden.ui.vault.feature.vault.util.toLoginIconData import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.combine @@ -319,8 +320,11 @@ class VerificationCodeViewModel @Inject constructor( authCodes: List, userAccount: UserState.Account?, ): DataState> { + val orgPremiumStatusMap = userAccount?.getOrganizationPremiumStatusMap() ?: emptyMap() val filteredAuthCodes = authCodes.mapNotNull { authCode -> - if (userAccount?.isPremium == true) { + val premiumStatus = + (authCode.orgId?.let { orgPremiumStatusMap[it] } ?: userAccount?.isPremium) == true + if (premiumStatus) { authCode } else { authCode.takeIf { it.orgUsesTotp } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index 3aae102b45f..95ce8bd2907 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -4380,6 +4380,7 @@ class AuthRepositoryTest { every { shouldUseKeyConnector } returns true every { type } returns OrganizationType.USER every { keyConnectorUrl } returns null + every { shouldUsersGetPremium } returns false }, ) fakeAuthDiskSource.storeOrganizations(userId = USER_ID_1, organizations = organizations) @@ -4405,6 +4406,7 @@ class AuthRepositoryTest { every { shouldUseKeyConnector } returns true every { type } returns OrganizationType.USER every { keyConnectorUrl } returns url + every { shouldUsersGetPremium } returns false }, ) fakeAuthDiskSource.storeOrganizations(userId = USER_ID_1, organizations = organizations) @@ -4441,6 +4443,7 @@ class AuthRepositoryTest { every { shouldUseKeyConnector } returns true every { type } returns OrganizationType.USER every { keyConnectorUrl } returns url + every { shouldUsersGetPremium } returns false }, ) fakeAuthDiskSource.storeOrganizations(userId = USER_ID_1, organizations = organizations) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt index f91e231cc79..07d1de2713f 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt @@ -193,6 +193,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -205,6 +206,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -217,6 +219,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -364,6 +367,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -395,6 +399,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -407,6 +412,7 @@ class AuthDiskSourceExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt index 4eed0b800bb..297ef092be3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt @@ -23,6 +23,7 @@ class SyncResponseJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), createMockOrganization(number = 1).toOrganization(), ) @@ -38,6 +39,7 @@ class SyncResponseJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = true, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "mockId-2", @@ -45,6 +47,7 @@ class SyncResponseJsonExtensionsTest { shouldManageResetPassword = true, shouldUseKeyConnector = false, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), listOf( diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt index 657e7a358a0..9bc1c1ee599 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt @@ -350,6 +350,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -414,6 +415,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -458,6 +460,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = true, @@ -518,6 +521,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -563,6 +567,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -631,6 +636,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -676,6 +682,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -744,6 +751,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -789,6 +797,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -857,6 +866,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), @@ -903,6 +913,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = true, shouldUseKeyConnector = false, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -974,6 +985,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = true, shouldUseKeyConnector = false, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), ), @@ -1178,6 +1190,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -1248,6 +1261,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), ), @@ -1293,6 +1307,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = false, @@ -1363,6 +1378,7 @@ class UserStateJsonExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerTest.kt index 3a1c2f4fdd5..924c278b9ee 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerTest.kt @@ -6,6 +6,7 @@ import com.bitwarden.vault.CipherView import com.bitwarden.vault.LoginView import com.x8bit.bitwarden.R 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.UserState import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.repository.SettingsRepository @@ -94,8 +95,10 @@ class AutofillTotpManagerTest { runTest { every { settingsRepository.isAutoCopyTotpDisabled } returns false every { cipherView.organizationUseTotp } returns false + every { cipherView.organizationId } returns null mutableUserStateFlow.value = mockk { every { activeAccount.isPremium } returns false + every { activeAccount.organizations } returns emptyList() } autofillTotpManager.tryCopyTotpToClipboard(cipherView = cipherView) @@ -115,8 +118,10 @@ class AutofillTotpManagerTest { runTest { every { settingsRepository.isAutoCopyTotpDisabled } returns false every { cipherView.organizationUseTotp } returns true + every { cipherView.organizationId } returns null mutableUserStateFlow.value = mockk { every { activeAccount.isPremium } returns true + every { activeAccount.organizations } returns emptyList() } every { loginView.totp } returns null @@ -141,8 +146,10 @@ class AutofillTotpManagerTest { ) every { settingsRepository.isAutoCopyTotpDisabled } returns false every { cipherView.organizationUseTotp } returns true + every { cipherView.organizationId } returns null mutableUserStateFlow.value = mockk { every { activeAccount.isPremium } returns true + every { activeAccount.organizations } returns emptyList() } every { loginView.totp } returns TOTP_CODE coEvery { @@ -160,6 +167,34 @@ class AutofillTotpManagerTest { vaultRepository.generateTotp(time = FIXED_CLOCK.instant(), totpCode = TOTP_CODE) } } + + @Suppress("MaxLineLength") + @Test + fun `tryCopyTotpToClipboard when isAutoCopyTotpDisabled is false, user has premium but item belongs to an org that doesn't should do nothing`() = + runTest { + val orgId = "orgId" + val mockOrganization = mockk(relaxed = true) { + every { id } returns orgId + every { shouldUsersGetPremium } returns false + } + every { settingsRepository.isAutoCopyTotpDisabled } returns false + every { cipherView.organizationUseTotp } returns false + every { cipherView.organizationId } returns orgId + mutableUserStateFlow.value = mockk { + every { activeAccount.isPremium } returns true + every { activeAccount.organizations } returns listOf(mockOrganization) + } + + autofillTotpManager.tryCopyTotpToClipboard(cipherView = cipherView) + + verify(exactly = 0) { + clipboardManager.setText(text = TOTP_RESULT_VALUE) + toast.show() + } + coVerify(exactly = 0) { + vaultRepository.generateTotp(time = FIXED_CLOCK.instant(), totpCode = TOTP_CODE) + } + } } private val FIXED_CLOCK: Clock = Clock.fixed( diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt index 3498970ac76..d41ea98185a 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherViewUtil.kt @@ -49,10 +49,12 @@ fun createMockCipherView( clock: Clock = FIXED_CLOCK, fido2Credentials: List? = null, sshKey: SshKeyView? = createMockSshKeyView(number = number), + organizationUsesTotp: Boolean = false, + organizationId: String? = "mockOrganizationId-$number", ): CipherView = CipherView( id = "mockId-$number", - organizationId = "mockOrganizationId-$number", + organizationId = organizationId, folderId = folderId, collectionIds = listOf("mockId-$number"), key = "mockKey-$number", @@ -85,7 +87,7 @@ fun createMockCipherView( reprompt = repromptType, secureNote = createMockSecureNoteView().takeIf { cipherType == CipherType.SECURE_NOTE }, edit = true, - organizationUseTotp = false, + organizationUseTotp = organizationUsesTotp, viewPassword = true, localData = null, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt index 0865ba6676a..6fb957e8179 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/TotpCodeManagerTest.kt @@ -48,7 +48,7 @@ class TotpCodeManagerTest { vaultSdkSource.generateTotp(any(), any(), any()) } returns totpResponse.asSuccess() - val expected = createVerificationCodeItem() + val expected = createVerificationCodeItem().copy(orgId = "mockOrganizationId-1") totpCodeManager.getTotpCodesStateFlow(userId, cipherList).test { assertEquals(DataState.Loaded(listOf(expected)), awaitItem()) @@ -106,7 +106,10 @@ class TotpCodeManagerTest { repromptType = CipherRepromptType.PASSWORD, ) - val expected = createVerificationCodeItem().copy(hasPasswordReprompt = true) + val expected = createVerificationCodeItem().copy( + hasPasswordReprompt = true, + orgId = cipherView.organizationId, + ) totpCodeManager.getTotpCodeStateFlow(userId, cipherView).test { assertEquals(DataState.Loaded(expected), awaitItem()) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt index 4a1657070a8..970dc0dba7c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt @@ -161,6 +161,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( shouldManageResetPassword = false, shouldUseKeyConnector = true, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), needsMasterPassword = false, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt index ac348df588e..c82a14d7ec9 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt @@ -386,6 +386,7 @@ class RootNavViewModelTest : BaseViewModelTest() { shouldManageResetPassword = false, shouldUseKeyConnector = true, role = OrganizationType.USER, + shouldUsersGetPremium = false, ), ), needsMasterPassword = false, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt index 734af84e63b..ae4308114c5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchScreenTest.kt @@ -938,6 +938,7 @@ private val DEFAULT_STATE: SearchState = SearchState( totpData = null, autofillSelectionData = null, isPremium = true, + organizationPremiumStatusMap = emptyMap(), ) private fun createStateForAutofill( 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 a0ec37d1dea..e4010db863f 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 @@ -1016,6 +1016,7 @@ class SearchViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) } returns expectedViewState val dataState = DataState.Loaded( @@ -1119,6 +1120,7 @@ class SearchViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) } returns expectedViewState mutableVaultDataStateFlow.tryEmit( @@ -1232,6 +1234,7 @@ class SearchViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) } returns expectedViewState val dataState = DataState.Error( @@ -1348,6 +1351,7 @@ class SearchViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) } returns expectedViewState val dataState = DataState.NoNetwork( @@ -1526,6 +1530,7 @@ class SearchViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) } returns expectedViewState val dataState = DataState.Loaded( @@ -1561,6 +1566,7 @@ private val DEFAULT_STATE: SearchState = SearchState( totpData = null, autofillSelectionData = null, isPremium = true, + organizationPremiumStatusMap = emptyMap(), ) private val DEFAULT_USER_STATE = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensionsTest.kt index ccecb1bcbf9..15e3d4a7747 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/util/SearchTypeDataExtensionsTest.kt @@ -326,6 +326,7 @@ class SearchTypeDataExtensionsTest { hasMasterPassword = true, isPremiumUser = true, isTotp = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals(SearchState.ViewState.Empty(message = null), result) @@ -352,6 +353,7 @@ class SearchTypeDataExtensionsTest { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -393,6 +395,7 @@ class SearchTypeDataExtensionsTest { hasMasterPassword = true, isPremiumUser = true, isTotp = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -444,6 +447,7 @@ class SearchTypeDataExtensionsTest { hasMasterPassword = true, isPremiumUser = true, isTotp = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( 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 0ae68a1016c..95620f0406d 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 @@ -4396,6 +4396,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = 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 ebd52cb3adf..a9716caef08 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 @@ -565,6 +565,7 @@ class CipherViewExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), isBiometricsEnabled = true, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt index 19200d37f67..da593a7890d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt @@ -2159,6 +2159,7 @@ private val DEFAULT_STATE = VaultItemListingState( hasMasterPassword = true, isPremium = false, isRefreshing = false, + organizationPremiumStatusMap = emptyMap(), ) private val STATE_FOR_AUTOFILL = DEFAULT_STATE.copy( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index b8130859234..9eb6fcec84e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -4079,6 +4079,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { fido2CreateCredentialRequest = null, isPremium = true, isRefreshing = false, + organizationPremiumStatusMap = emptyMap(), ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt index f3393042623..e701ae2f0a3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensionsTest.kt @@ -464,6 +464,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -557,6 +558,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = fido2CredentialAutofillViews, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -643,6 +645,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = fido2CredentialAutofillViews, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -702,6 +705,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) @@ -726,6 +730,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) @@ -748,6 +753,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) @@ -770,6 +776,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) @@ -796,6 +803,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) @@ -824,6 +832,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) @@ -850,6 +859,7 @@ class VaultItemListingDataExtensionsTest { every { issuer } returns "issuer" }, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ), ) } @@ -1054,6 +1064,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -1098,6 +1109,7 @@ class VaultItemListingDataExtensionsTest { fido2CredentialAutofillViews = null, totpData = null, isPremiumUser = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt index abaad2e4de1..0fd0305ab10 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt @@ -496,6 +496,7 @@ private val DEFAULT_USER_STATE = UserState( shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "mockOrganizationId-2", @@ -503,6 +504,7 @@ private val DEFAULT_USER_STATE = UserState( shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "mockOrganizationId-3", @@ -510,6 +512,7 @@ private val DEFAULT_USER_STATE = UserState( shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt index 900497733cd..f470b2043d3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt @@ -108,6 +108,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "mockOrganizationId-2", @@ -115,6 +116,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "mockOrganizationId-3", @@ -122,6 +124,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ) } else { diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt index 33c4bcddbd3..4efa79d28ca 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreenTest.kt @@ -1329,6 +1329,7 @@ private val DEFAULT_STATE: VaultState = VaultState( isRefreshing = false, showImportActionCard = false, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) private val DEFAULT_CONTENT_VIEW_STATE: VaultState.ViewState.Content = VaultState.ViewState.Content( 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 fc76e73986b..a7941c6bcf4 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 @@ -230,6 +230,7 @@ class VaultViewModelTest : BaseViewModelTest() { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -316,6 +317,7 @@ class VaultViewModelTest : BaseViewModelTest() { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -526,6 +528,7 @@ class VaultViewModelTest : BaseViewModelTest() { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = true, ), ), ), @@ -541,7 +544,9 @@ class VaultViewModelTest : BaseViewModelTest() { baseIconUrl = viewModel.stateFlow.value.baseIconUrl, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = mapOf("testOrganizationId" to true), ), + organizationPremiumStatusMap = mapOf("testOrganizationId" to true), ) .copy( appBarTitle = R.string.vaults.asText(), @@ -554,20 +559,22 @@ class VaultViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultAction.VaultFilterTypeSelect(VaultFilterType.MyVault)) - assertEquals( - initialState.copy( - vaultFilterData = VAULT_FILTER_DATA.copy( - selectedVaultFilterType = VaultFilterType.MyVault, - ), - viewState = vaultData.toViewState( - isPremium = true, - vaultFilterType = VaultFilterType.MyVault, - isIconLoadingDisabled = viewModel.stateFlow.value.isIconLoadingDisabled, - baseIconUrl = viewModel.stateFlow.value.baseIconUrl, - hasMasterPassword = true, - showSshKeys = false, - ), + val resultingState = initialState.copy( + vaultFilterData = VAULT_FILTER_DATA.copy( + selectedVaultFilterType = VaultFilterType.MyVault, + ), + viewState = vaultData.toViewState( + isPremium = true, + vaultFilterType = VaultFilterType.MyVault, + isIconLoadingDisabled = viewModel.stateFlow.value.isIconLoadingDisabled, + baseIconUrl = viewModel.stateFlow.value.baseIconUrl, + hasMasterPassword = true, + showSshKeys = false, + organizationPremiumStatusMap = mapOf("testOrganizationId" to true), ), + ) + assertEquals( + resultingState, viewModel.stateFlow.value, ) verify { vaultRepository.vaultFilterType = VaultFilterType.MyVault } @@ -671,7 +678,7 @@ class VaultViewModelTest : BaseViewModelTest() { ), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = CipherType.entries.size, sshKeyItemsCount = 1, ), @@ -696,7 +703,7 @@ class VaultViewModelTest : BaseViewModelTest() { collectionItems = listOf(), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -810,7 +817,7 @@ class VaultViewModelTest : BaseViewModelTest() { ), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -910,7 +917,7 @@ class VaultViewModelTest : BaseViewModelTest() { ), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -976,7 +983,12 @@ class VaultViewModelTest : BaseViewModelTest() { mutableVaultDataStateFlow.tryEmit( value = DataState.NoNetwork( data = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1)), + cipherViewList = listOf( + createMockCipherView( + number = 1, + organizationUsesTotp = true, + ), + ), collectionViewList = listOf(createMockCollectionView(number = 1)), folderViewList = listOf(createMockFolderView(number = 1)), sendViewList = listOf(createMockSendView(number = 1)), @@ -1116,7 +1128,7 @@ class VaultViewModelTest : BaseViewModelTest() { collectionItems = listOf(), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = CipherType.entries.size - 1, sshKeyItemsCount = 0, ), @@ -1157,7 +1169,7 @@ class VaultViewModelTest : BaseViewModelTest() { collectionItems = listOf(), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = CipherType.entries.size, sshKeyItemsCount = 1, ), @@ -1915,6 +1927,7 @@ private fun createMockVaultState( viewState: VaultState.ViewState, dialog: VaultState.DialogState? = null, showSshKeys: Boolean = false, + organizationPremiumStatusMap: Map = emptyMap(), ): VaultState = VaultState( appBarTitle = R.string.my_vault.asText(), @@ -1953,4 +1966,5 @@ private fun createMockVaultState( showImportActionCard = true, isRefreshing = false, showSshKeys = showSshKeys, + organizationPremiumStatusMap = organizationPremiumStatusMap, ) 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 4f268160940..e9f1f57f6ac 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 @@ -82,6 +82,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -109,6 +110,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -140,6 +142,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -171,6 +174,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -217,6 +221,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -261,6 +266,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -309,6 +315,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -387,6 +394,7 @@ class UserStateExtensionsTest { shouldUseKeyConnector = false, shouldManageResetPassword = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "organizationId-A", @@ -394,6 +402,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -445,6 +454,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), Organization( id = "organizationId-A", @@ -452,6 +462,7 @@ class UserStateExtensionsTest { shouldManageResetPassword = false, shouldUseKeyConnector = false, role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, ), ), trustedDevice = null, @@ -465,4 +476,51 @@ class UserStateExtensionsTest { ), ) } + + @Test + fun `getOrganizationPremiumStatusMap should map organizations to correct status`() { + val actualMap = UserState.Account( + userId = "userId", + name = "name", + email = "email", + avatarColorHex = "avatarColorHex", + environment = Environment.Us, + isPremium = false, + isLoggedIn = true, + isVaultUnlocked = false, + needsPasswordReset = false, + isBiometricsEnabled = false, + needsMasterPassword = false, + organizations = listOf( + Organization( + id = "1", + name = "organizationName", + shouldManageResetPassword = false, + shouldUseKeyConnector = false, + role = OrganizationType.ADMIN, + shouldUsersGetPremium = false, + ), Organization( + id = "2", + name = "organizationName", + shouldManageResetPassword = false, + shouldUseKeyConnector = false, + role = OrganizationType.ADMIN, + shouldUsersGetPremium = true, + ), + ), + trustedDevice = null, + hasMasterPassword = true, + isUsingKeyConnector = false, + onboardingStatus = OnboardingStatus.COMPLETE, + firstTimeState = FirstTimeState(showImportLoginsCard = true), + ).getOrganizationPremiumStatusMap() + + assertEquals( + mapOf( + "1" to false, + "2" to true, + ), + actualMap, + ) + } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt index 21540694f2f..17514538332 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensionsTest.kt @@ -62,6 +62,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -98,7 +99,7 @@ class VaultDataExtensionsTest { ), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -126,6 +127,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.MyVault, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -182,6 +184,7 @@ class VaultDataExtensionsTest { ), hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -207,7 +210,7 @@ class VaultDataExtensionsTest { ), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -231,6 +234,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -255,6 +259,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -265,9 +270,9 @@ class VaultDataExtensionsTest { @Suppress("MaxLineLength") @Test - fun `toViewState should return 1 for totpItemsCount if user has premium and has one totp item`() { + fun `toViewState should return 1 for totpItemsCount if user has premium and has one totp item and item is owned by user`() { val vaultData = VaultData( - cipherViewList = listOf(createMockCipherView(number = 1)), + cipherViewList = listOf(createMockCipherView(number = 1, organizationId = null)), collectionViewList = listOf(), folderViewList = listOf(), sendViewList = listOf(), @@ -280,6 +285,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -318,6 +324,7 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -356,6 +363,7 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -396,6 +404,7 @@ class VaultDataExtensionsTest { baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -620,6 +629,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -633,7 +643,7 @@ class VaultDataExtensionsTest { collectionItems = listOf(), noFolderItems = listOf(), trashItemsCount = 2, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -660,6 +670,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -689,7 +700,7 @@ class VaultDataExtensionsTest { every { uriMock.host } returns "www.mockuri1.com" val vaultData = VaultData( cipherViewList = List(100) { - createMockCipherView(number = it, folderId = null) + createMockCipherView(number = it, folderId = null, organizationUsesTotp = true) }, collectionViewList = listOf(), folderViewList = listOf(), @@ -703,6 +714,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -759,6 +771,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -800,7 +813,7 @@ class VaultDataExtensionsTest { ), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, itemTypesCount = 4, sshKeyItemsCount = 0, ), @@ -828,6 +841,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = false, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -843,7 +857,7 @@ class VaultDataExtensionsTest { folderItems = listOf(), noFolderItems = listOf(), trashItemsCount = 0, - totpItemsCount = 1, + totpItemsCount = 0, // Verify item types count excludes CipherType.SSH_KEY when showSshKeys is false. itemTypesCount = 4, ), @@ -855,7 +869,7 @@ class VaultDataExtensionsTest { fun `toViewState should include SSH key vault items and type count if showSshKeys is true`() { val vaultData = VaultData( cipherViewList = listOf( - createMockCipherView(number = 1), + createMockCipherView(number = 1, organizationId = null), createMockCipherView(number = 2, cipherType = CipherType.SSH_KEY), ), collectionViewList = listOf(), @@ -871,6 +885,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( @@ -929,6 +944,7 @@ class VaultDataExtensionsTest { vaultFilterType = VaultFilterType.AllVaults, hasMasterPassword = true, showSshKeys = true, + organizationPremiumStatusMap = emptyMap(), ) assertEquals( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt index ef1423a9810..c77706f3fb0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt @@ -57,6 +57,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { private val mockUserAccount: UserState.Account = mockk { every { isPremium } returns true + every { organizations } returns emptyList() } private val mockUserState: UserState = mockk { diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt index ba8fbe40146..f402394a1da 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/util/VerificationCodeDataUtil.kt @@ -16,4 +16,5 @@ fun createVerificationCodeItem(number: Int = 1) = username = "mockUsername-$number", hasPasswordReprompt = false, orgUsesTotp = false, + orgId = null, ) From 129fa274edde7f3f270c3018247d46d5243cd454 Mon Sep 17 00:00:00 2001 From: Dave Severns Date: Mon, 2 Dec 2024 14:43:46 -0500 Subject: [PATCH 2/2] orEmpty() + formtatting --- .../data/autofill/manager/AutofillTotpManagerImpl.kt | 2 +- .../ui/vault/feature/vault/util/VaultDataExtensions.kt | 10 ++++------ .../verificationcode/VerificationCodeViewModel.kt | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt index ef4af1babab..deef6c9c4d0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/manager/AutofillTotpManagerImpl.kt @@ -30,7 +30,7 @@ class AutofillTotpManagerImpl( .value ?.activeAccount ?.getOrganizationPremiumStatusMap() - ?: emptyMap() + .orEmpty() val isPremium = authRepository.userStateFlow.value?.activeAccount?.isPremium == true val premiumStatus = organizationPremiumStatusMap[cipherView.organizationId] ?: isPremium if (!premiumStatus && !cipherView.organizationUseTotp) return diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt index 259ae92b123..d1c377b84e6 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/VaultDataExtensions.kt @@ -80,14 +80,12 @@ fun VaultData.toViewState( val userOwnedTotpItems = totpItemsGroupedByOwnership[false] ?.filter { it.login?.totp != null && isPremium - } - ?: emptyList() + }.orEmpty() val organizationOwnedTotpItems = totpItemsGroupedByOwnership[true] ?.filter { - it.login?.totp != null && - (organizationPremiumStatusMap[it.id] == true || it.organizationUseTotp) - } - ?: emptyList() + it.login?.totp != null && + (organizationPremiumStatusMap[it.id] == true || it.organizationUseTotp) + }.orEmpty() VaultState.ViewState.Content( itemTypesCount = itemTypesCount, totpItemsCount = userOwnedTotpItems.count() + diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt index 900971f2fb5..4b27da93a90 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt @@ -320,7 +320,7 @@ class VerificationCodeViewModel @Inject constructor( authCodes: List, userAccount: UserState.Account?, ): DataState> { - val orgPremiumStatusMap = userAccount?.getOrganizationPremiumStatusMap() ?: emptyMap() + val orgPremiumStatusMap = userAccount?.getOrganizationPremiumStatusMap().orEmpty() val filteredAuthCodes = authCodes.mapNotNull { authCode -> val premiumStatus = (authCode.orgId?.let { orgPremiumStatusMap[it] } ?: userAccount?.isPremium) == true