mirror of
https://github.com/bitwarden/android.git
synced 2026-08-02 21:52:21 -05:00
[PM-18092] Update cipher delete restore permissions (#5075)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
This commit is contained in:
co-authored by
Patrick Honkonen
parent
639ca02739
commit
1a2beea770
@@ -41,6 +41,7 @@ sealed class FlagKey<out T : Any> {
|
||||
ChromeAutofill,
|
||||
MobileErrorReporting,
|
||||
FlightRecorder,
|
||||
RestrictCipherItemDeletion,
|
||||
PreAuthSettings,
|
||||
)
|
||||
}
|
||||
@@ -194,6 +195,15 @@ sealed class FlagKey<out T : Any> {
|
||||
override val isRemotelyConfigured: Boolean = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key to enable the restriction of cipher item deletion
|
||||
*/
|
||||
data object RestrictCipherItemDeletion : FlagKey<Boolean>() {
|
||||
override val keyName: String = "pm-15493-restrict-item-deletion-to-can-manage-permission"
|
||||
override val defaultValue: Boolean = false
|
||||
override val isRemotelyConfigured: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key to enable the settings menu before login.
|
||||
*/
|
||||
|
||||
+11
-2
@@ -4,6 +4,7 @@ import com.bitwarden.core.annotation.OmitFromCoverage
|
||||
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.vault.Cipher
|
||||
import com.bitwarden.vault.CipherListView
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials
|
||||
@@ -24,15 +25,23 @@ class Fido2CredentialStoreImpl(
|
||||
/**
|
||||
* Return all active ciphers that contain FIDO 2 credentials.
|
||||
*/
|
||||
override suspend fun allCredentials(): List<CipherView> {
|
||||
override suspend fun allCredentials(): List<CipherListView> {
|
||||
val syncResult = vaultRepository.syncForResult()
|
||||
if (syncResult is SyncVaultDataResult.Error) {
|
||||
syncResult.throwable
|
||||
?.let { throw it }
|
||||
?: throw IllegalStateException("Sync failed.")
|
||||
}
|
||||
return vaultRepository.ciphersStateFlow.value.data
|
||||
val activeCipherIds = vaultRepository.ciphersStateFlow.value.data
|
||||
?.filter { it.isActiveWithFido2Credentials }
|
||||
?.map { it.id }
|
||||
?: emptyList()
|
||||
|
||||
return vaultRepository.ciphersListViewStateFlow.value.data
|
||||
?.filter { clv ->
|
||||
activeCipherIds
|
||||
.contains(clv.id)
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||
import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.send.SendType
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.vault.CipherListView
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.bitwarden.vault.CollectionView
|
||||
import com.bitwarden.vault.FolderView
|
||||
@@ -65,6 +66,14 @@ interface VaultRepository : CipherManager, VaultLockManager {
|
||||
*/
|
||||
val ciphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||
|
||||
/**
|
||||
* Flow that represents all ciphers for the active user.
|
||||
*
|
||||
* Note that the [StateFlow.value] will return the last known value but the [StateFlow] itself
|
||||
* must be collected in order to trigger state changes.
|
||||
*/
|
||||
val ciphersListViewStateFlow: StateFlow<DataState<List<CipherListView>>>
|
||||
|
||||
/**
|
||||
* Flow that represents all collections for the active user.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.bitwarden.sdk.Fido2CredentialStore
|
||||
import com.bitwarden.send.Send
|
||||
import com.bitwarden.send.SendType
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.vault.CipherListView
|
||||
import com.bitwarden.vault.CipherType
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.bitwarden.vault.CollectionView
|
||||
@@ -169,6 +170,9 @@ class VaultRepositoryImpl(
|
||||
private val mutableCiphersStateFlow =
|
||||
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
|
||||
|
||||
private val mutableCiphersListViewStateFlow =
|
||||
MutableStateFlow<DataState<List<CipherListView>>>(DataState.Loading)
|
||||
|
||||
private val mutableFoldersStateFlow =
|
||||
MutableStateFlow<DataState<List<FolderView>>>(DataState.Loading)
|
||||
|
||||
@@ -214,6 +218,9 @@ class VaultRepositoryImpl(
|
||||
override val ciphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||
get() = mutableCiphersStateFlow.asStateFlow()
|
||||
|
||||
override val ciphersListViewStateFlow: StateFlow<DataState<List<CipherListView>>>
|
||||
get() = mutableCiphersListViewStateFlow.asStateFlow()
|
||||
|
||||
override val domainsStateFlow: StateFlow<DataState<DomainsData>>
|
||||
get() = mutableDomainsStateFlow.asStateFlow()
|
||||
|
||||
@@ -260,6 +267,15 @@ class VaultRepositoryImpl(
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
|
||||
mutableCiphersListViewStateFlow
|
||||
.observeWhenSubscribedAndUnlocked(
|
||||
userStateFlow = authDiskSource.userStateFlow,
|
||||
vaultUnlockFlow = vaultUnlockDataStateFlow,
|
||||
) { activeUserId ->
|
||||
observeVaultDiskCiphersToCipherListView(activeUserId)
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
|
||||
// Setup domains MutableStateFlow
|
||||
mutableDomainsStateFlow
|
||||
.observeWhenSubscribedAndLoggedIn(
|
||||
@@ -1071,6 +1087,27 @@ class VaultRepositoryImpl(
|
||||
.map { it.orLoadingIfNotSynced(userId = userId) }
|
||||
.onEach { mutableCiphersStateFlow.value = it }
|
||||
|
||||
private fun observeVaultDiskCiphersToCipherListView(
|
||||
userId: String,
|
||||
): Flow<DataState<List<CipherListView>>> =
|
||||
vaultDiskSource
|
||||
.getCiphers(userId = userId)
|
||||
.onStart { mutableCiphersListViewStateFlow.updateToPendingOrLoading() }
|
||||
.map {
|
||||
waitUntilUnlocked(userId = userId)
|
||||
vaultSdkSource
|
||||
.decryptCipherListCollection(
|
||||
userId = userId,
|
||||
cipherList = it.toEncryptedSdkCipherList(),
|
||||
)
|
||||
.fold(
|
||||
onSuccess = { ciphers -> DataState.Loaded(ciphers.sortAlphabetically()) },
|
||||
onFailure = { throwable -> DataState.Error(throwable) },
|
||||
)
|
||||
}
|
||||
.map { it.orLoadingIfNotSynced(userId = userId) }
|
||||
.onEach { mutableCiphersListViewStateFlow.value = it }
|
||||
|
||||
private fun observeVaultDiskDomains(
|
||||
userId: String,
|
||||
): Flow<DataState<DomainsData>> =
|
||||
|
||||
+38
@@ -15,6 +15,8 @@ import com.bitwarden.network.model.UriMatchTypeJson
|
||||
import com.bitwarden.vault.Attachment
|
||||
import com.bitwarden.vault.Card
|
||||
import com.bitwarden.vault.Cipher
|
||||
import com.bitwarden.vault.CipherListView
|
||||
import com.bitwarden.vault.CipherPermissions
|
||||
import com.bitwarden.vault.CipherRepromptType
|
||||
import com.bitwarden.vault.CipherType
|
||||
import com.bitwarden.vault.CipherView
|
||||
@@ -68,6 +70,7 @@ fun Cipher.toEncryptedNetworkCipherResponse(): SyncResponseJson.Cipher =
|
||||
notes = notes,
|
||||
reprompt = reprompt.toNetworkRepromptType(),
|
||||
passwordHistory = passwordHistory?.toEncryptedNetworkPasswordHistoryList(),
|
||||
permissions = permissions?.toEncryptedNetworkCipherPermissions(),
|
||||
type = type.toNetworkCipherType(),
|
||||
login = login?.toEncryptedNetworkLogin(),
|
||||
secureNote = secureNote?.toEncryptedNetworkSecureNote(),
|
||||
@@ -299,6 +302,17 @@ private fun PasswordHistory.toEncryptedNetworkPasswordHistory(): SyncResponseJso
|
||||
lastUsedDate = ZonedDateTime.ofInstant(lastUsedDate, ZoneOffset.UTC),
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a Bitwarden SDK [CipherPermissions] object to a corresponding
|
||||
* [SyncResponseJson.Cipher.CipherPermissions] object.
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
private fun CipherPermissions.toEncryptedNetworkCipherPermissions(): SyncResponseJson.Cipher.CipherPermissions =
|
||||
SyncResponseJson.Cipher.CipherPermissions(
|
||||
delete = delete,
|
||||
restore = restore,
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a Bitwarden SDK [CipherRepromptType] object to a corresponding
|
||||
* [CipherRepromptTypeJson] object.
|
||||
@@ -357,6 +371,7 @@ fun SyncResponseJson.Cipher.toEncryptedSdkCipher(): Cipher =
|
||||
attachments = attachments?.toSdkAttachmentList(),
|
||||
fields = fields?.toSdkFieldList(),
|
||||
passwordHistory = passwordHistory?.toSdkPasswordHistoryList(),
|
||||
permissions = permissions?.toSdkPermissions(),
|
||||
creationDate = creationDate.toInstant(),
|
||||
deletedDate = deletedDate?.toInstant(),
|
||||
revisionDate = revisionDate.toInstant(),
|
||||
@@ -531,6 +546,16 @@ fun SyncResponseJson.Cipher.PasswordHistory.toSdkPasswordHistory(): PasswordHist
|
||||
lastUsedDate = lastUsedDate.toInstant(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [SyncResponseJson.Cipher.CipherPermissions] into
|
||||
* a corresponding Bitwarden SDK [CipherPermissions].
|
||||
*/
|
||||
fun SyncResponseJson.Cipher.CipherPermissions.toSdkPermissions(): CipherPermissions =
|
||||
CipherPermissions(
|
||||
delete = delete,
|
||||
restore = restore,
|
||||
)
|
||||
|
||||
/**
|
||||
* Transforms a [CipherTypeJson] to the corresponding Bitwarden SDK [CipherType].
|
||||
*/
|
||||
@@ -588,3 +613,16 @@ fun List<CipherView>.sortAlphabetically(): List<CipherView> {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the data in alphabetical order by name. Using lexicographical sorting but giving
|
||||
* precedence to special characters over letters and digits.
|
||||
*/
|
||||
@JvmName("toAlphabeticallySortedCipherListView")
|
||||
fun List<CipherListView>.sortAlphabetically(): List<CipherListView> {
|
||||
return this.sortedWith(
|
||||
comparator = { cipher1, cipher2 ->
|
||||
SpecialCharWithPrecedenceComparator.compare(cipher1.name, cipher2.name)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+2
@@ -41,6 +41,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.ChromeAutofill,
|
||||
FlagKey.MobileErrorReporting,
|
||||
FlagKey.FlightRecorder,
|
||||
FlagKey.RestrictCipherItemDeletion,
|
||||
FlagKey.PreAuthSettings,
|
||||
-> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -102,5 +103,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.ChromeAutofill -> stringResource(R.string.enable_chrome_autofill)
|
||||
FlagKey.MobileErrorReporting -> stringResource(R.string.enable_error_reporting_dialog)
|
||||
FlagKey.FlightRecorder -> stringResource(R.string.enable_flight_recorder)
|
||||
FlagKey.RestrictCipherItemDeletion -> stringResource(R.string.restrict_item_deletion)
|
||||
FlagKey.PreAuthSettings -> stringResource(R.string.enable_pre_auth_settings)
|
||||
}
|
||||
|
||||
+21
-4
@@ -26,12 +26,14 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResu
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.util.getCreatePasskeyCredentialRequestOrNull
|
||||
import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
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.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.CoachMarkTourType
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull
|
||||
@@ -119,6 +121,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
private val organizationEventManager: OrganizationEventManager,
|
||||
private val networkConnectionManager: NetworkConnectionManager,
|
||||
private val firstTimeActionManager: FirstTimeActionManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>(
|
||||
// We load the state from the savedStateHandle for testing purposes.
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
@@ -1719,6 +1722,10 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
vaultData: VaultData?,
|
||||
userData: UserState?,
|
||||
): VaultAddEditState {
|
||||
val restrictCipherItemDeletionEnabled = featureFlagManager
|
||||
.getFeatureFlag(
|
||||
FlagKey.RestrictCipherItemDeletion,
|
||||
)
|
||||
val internalVaultData = vaultData
|
||||
?: VaultData(
|
||||
cipherViewList = emptyList(),
|
||||
@@ -1737,10 +1744,15 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
currentAccount = userData?.activeAccount,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
) { currentAccount, cipherView ->
|
||||
|
||||
val canDelete = internalVaultData
|
||||
.collectionViewList
|
||||
.hasDeletePermissionInAtLeastOneCollection(cipherView?.collectionIds)
|
||||
val canDelete = if (restrictCipherItemDeletionEnabled &&
|
||||
cipherView?.permissions?.delete != null
|
||||
) {
|
||||
cipherView.permissions?.delete == true
|
||||
} else {
|
||||
internalVaultData
|
||||
.collectionViewList
|
||||
.hasDeletePermissionInAtLeastOneCollection(cipherView?.collectionIds)
|
||||
}
|
||||
|
||||
val canAssignToCollections = internalVaultData
|
||||
.collectionViewList
|
||||
@@ -2241,16 +2253,21 @@ data class VaultAddEditState(
|
||||
* This is only present when editing a pre-existing cipher.
|
||||
* @property name Represents the name for the item type. This is an abstract property
|
||||
* that must be overridden to save the item.
|
||||
* @property isUnlockWithPasswordEnabled Indicates whether the user is allowed to
|
||||
* unlock with a password.
|
||||
* @property masterPasswordReprompt Indicates if a master password reprompt is required.
|
||||
* @property favorite Indicates whether this item is marked as a favorite.
|
||||
* @property customFieldData Additional custom fields associated with the item.
|
||||
* @property notes Any additional notes or comments associated with the item.
|
||||
* @property selectedCollectionId The ID of the collection that this item belongs to.
|
||||
* @property selectedFolderId The ID of the folder that this item belongs to.
|
||||
* @property availableFolders The list of folders that this item could be added too.
|
||||
* @property selectedOwnerId The ID of the owner associated with the item.
|
||||
* @property availableOwners A list of available owners.
|
||||
* @property hasOrganizations Indicates if the user is part of any organizations.
|
||||
* @property canDelete Indicates whether the current user can delete the item.
|
||||
* @property canAssignToCollections Indicates whether the current user can assign the
|
||||
* item to a collection.
|
||||
*/
|
||||
@Parcelize
|
||||
data class Common(
|
||||
|
||||
@@ -166,7 +166,7 @@ fun VaultItemScreen(
|
||||
{ viewModel.trySendAction(VaultItemAction.Common.CloseClick) }
|
||||
},
|
||||
actions = {
|
||||
if (state.isCipherDeleted && state.canDelete) {
|
||||
if (state.canRestore) {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.restore),
|
||||
onClick = remember(viewModel) {
|
||||
|
||||
@@ -17,8 +17,10 @@ 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.auth.repository.model.ValidatePasswordResult
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
@@ -70,6 +72,7 @@ class VaultItemViewModel @Inject constructor(
|
||||
private val organizationEventManager: OrganizationEventManager,
|
||||
private val environmentRepository: EnvironmentRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
) : BaseViewModel<VaultItemState, VaultItemEvent, VaultItemAction>(
|
||||
// We load the state from the savedStateHandle for testing purposes.
|
||||
initialState = savedStateHandle[KEY_STATE] ?: run {
|
||||
@@ -105,6 +108,10 @@ class VaultItemViewModel @Inject constructor(
|
||||
vaultRepository.collectionsStateFlow,
|
||||
vaultRepository.foldersStateFlow,
|
||||
) { cipherViewState, userState, authCodeState, collectionsState, folderState ->
|
||||
val restrictCipherItemDeletionEnabled = featureFlagManager
|
||||
.getFeatureFlag(
|
||||
FlagKey.RestrictCipherItemDeletion,
|
||||
)
|
||||
val totpCodeData = authCodeState.data?.let {
|
||||
TotpCodeItemData(
|
||||
periodSeconds = it.periodSeconds,
|
||||
@@ -126,10 +133,24 @@ class VaultItemViewModel @Inject constructor(
|
||||
}
|
||||
.mapNullable {
|
||||
val cipherView = cipherViewState.data
|
||||
val canDelete = collectionsState.data
|
||||
.hasDeletePermissionInAtLeastOneCollection(
|
||||
val canDelete = if (restrictCipherItemDeletionEnabled &&
|
||||
cipherView?.permissions?.delete != null
|
||||
) {
|
||||
cipherView.permissions?.delete == true
|
||||
} else {
|
||||
collectionsState.data.hasDeletePermissionInAtLeastOneCollection(
|
||||
collectionIds = cipherView?.collectionIds,
|
||||
)
|
||||
}
|
||||
|
||||
val canRestore = if (restrictCipherItemDeletionEnabled &&
|
||||
cipherView?.permissions?.restore != null
|
||||
) {
|
||||
cipherView.permissions?.restore == true &&
|
||||
cipherView.deletedDate != null
|
||||
} else {
|
||||
canDelete && cipherView?.deletedDate != null
|
||||
}
|
||||
|
||||
val canAssignToCollections = collectionsState.data
|
||||
.canAssignToCollections(cipherView?.collectionIds)
|
||||
@@ -167,6 +188,7 @@ class VaultItemViewModel @Inject constructor(
|
||||
cipher = cipherView,
|
||||
totpCodeItemData = totpCodeData,
|
||||
canDelete = canDelete,
|
||||
canRestore = canRestore,
|
||||
canAssociateToCollections = canAssignToCollections,
|
||||
canEdit = canEdit,
|
||||
relatedLocations = relatedLocations,
|
||||
@@ -1200,6 +1222,7 @@ class VaultItemViewModel @Inject constructor(
|
||||
hasMasterPassword = account.hasMasterPassword,
|
||||
totpCodeItemData = this.data?.totpCodeItemData,
|
||||
canDelete = this.data?.canDelete == true,
|
||||
canRestore = this.data?.canRestore == true,
|
||||
canAssignToCollections = this.data?.canAssociateToCollections == true,
|
||||
canEdit = this.data?.canEdit == true,
|
||||
baseIconUrl = environmentRepository.environment.environmentUrlData.baseIconUrl,
|
||||
@@ -1496,6 +1519,14 @@ data class VaultItemState(
|
||||
?.common
|
||||
?.canDelete == true
|
||||
|
||||
/**
|
||||
* Whether or not the cipher can be deleted.
|
||||
*/
|
||||
val canRestore: Boolean
|
||||
get() = viewState.asContentOrNull()
|
||||
?.common
|
||||
?.canRestore == true
|
||||
|
||||
val canAssignToCollections: Boolean
|
||||
get() = viewState.asContentOrNull()
|
||||
?.common
|
||||
@@ -1555,6 +1586,7 @@ data class VaultItemState(
|
||||
* @property currentCipher The cipher that is currently being viewed (nullable).
|
||||
* @property attachments A list of attachments associated with the cipher.
|
||||
* @property canDelete Indicates if the cipher can be deleted.
|
||||
* @property canRestore Indicates if the cipher can be restored.
|
||||
* @property canAssignToCollections Indicates if the cipher can be assigned to
|
||||
* collections.
|
||||
* @property favorite Indicates that the cipher is favorite.
|
||||
@@ -1572,6 +1604,7 @@ data class VaultItemState(
|
||||
val currentCipher: CipherView? = null,
|
||||
val attachments: List<AttachmentItem>?,
|
||||
val canDelete: Boolean,
|
||||
val canRestore: Boolean,
|
||||
val canAssignToCollections: Boolean,
|
||||
val canEdit: Boolean,
|
||||
val favorite: Boolean,
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ data class VaultItemStateData(
|
||||
val cipher: CipherView?,
|
||||
val totpCodeItemData: TotpCodeItemData?,
|
||||
val canDelete: Boolean,
|
||||
val canRestore: Boolean,
|
||||
val canAssociateToCollections: Boolean,
|
||||
val canEdit: Boolean,
|
||||
val relatedLocations: ImmutableList<VaultItemLocation>,
|
||||
|
||||
+2
@@ -45,6 +45,7 @@ fun CipherView.toViewState(
|
||||
totpCodeItemData: TotpCodeItemData?,
|
||||
clock: Clock = Clock.systemDefaultZone(),
|
||||
canDelete: Boolean,
|
||||
canRestore: Boolean,
|
||||
canAssignToCollections: Boolean,
|
||||
canEdit: Boolean,
|
||||
baseIconUrl: String,
|
||||
@@ -98,6 +99,7 @@ fun CipherView.toViewState(
|
||||
}
|
||||
.orEmpty(),
|
||||
canDelete = canDelete,
|
||||
canRestore = canRestore,
|
||||
canAssignToCollections = canAssignToCollections,
|
||||
canEdit = canEdit,
|
||||
favorite = this.favorite,
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ fun VaultAddEditState.ViewState.Content.toCipherView(): CipherView =
|
||||
attachments = common.originalCipher?.attachments,
|
||||
organizationUseTotp = common.originalCipher?.organizationUseTotp ?: false,
|
||||
passwordHistory = toPasswordHistory(),
|
||||
permissions = common.originalCipher?.permissions,
|
||||
creationDate = common.originalCipher?.creationDate ?: Instant.now(),
|
||||
deletedDate = common.originalCipher?.deletedDate,
|
||||
revisionDate = common.originalCipher?.revisionDate ?: Instant.now(),
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<string name="enable_chrome_autofill">Enable chrome autofill</string>
|
||||
<string name="enable_error_reporting_dialog">Enable error reporting dialog</string>
|
||||
<string name="enable_flight_recorder">Enable flight recorder</string>
|
||||
<string name="restrict_item_deletion">Restrict item deletion</string>
|
||||
<string name="enable_pre_auth_settings">Enabled pre-auth settings</string>
|
||||
<!-- /Debug Menu -->
|
||||
</resources>
|
||||
|
||||
@@ -69,6 +69,10 @@ class FlagKeyTest {
|
||||
FlagKey.FlightRecorder.keyName,
|
||||
"enable-pm-flight-recorder",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.RestrictCipherItemDeletion.keyName,
|
||||
"pm-15493-restrict-item-deletion-to-can-manage-permission",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.PreAuthSettings.keyName,
|
||||
"enable-pm-prelogin-settings",
|
||||
@@ -94,6 +98,7 @@ class FlagKeyTest {
|
||||
FlagKey.ChromeAutofill,
|
||||
FlagKey.MobileErrorReporting,
|
||||
FlagKey.FlightRecorder,
|
||||
FlagKey.RestrictCipherItemDeletion,
|
||||
FlagKey.PreAuthSettings,
|
||||
).all {
|
||||
!it.defaultValue
|
||||
@@ -129,6 +134,7 @@ class FlagKeyTest {
|
||||
listOf(
|
||||
FlagKey.FlightRecorder,
|
||||
FlagKey.PreAuthSettings,
|
||||
FlagKey.RestrictCipherItemDeletion,
|
||||
).all {
|
||||
!it.isRemotelyConfigured
|
||||
},
|
||||
|
||||
+4
@@ -326,6 +326,10 @@ private const val CIPHER_JSON = """
|
||||
"lastUsedDate": "2023-10-27T12:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"permissions": {
|
||||
"delete": true,
|
||||
"restore": true
|
||||
},
|
||||
"revisionDate": "2023-10-27T12:00:00.000Z",
|
||||
"type": 1,
|
||||
"login": {
|
||||
|
||||
+1
@@ -83,6 +83,7 @@ fun createMockCipherView(
|
||||
sshKey = sshKey.takeIf { cipherType == CipherType.SSH_KEY },
|
||||
favorite = false,
|
||||
passwordHistory = listOf(createMockPasswordHistoryView(number = number, clock)),
|
||||
permissions = createMockSdkCipherPermissions(),
|
||||
reprompt = repromptType,
|
||||
secureNote = createMockSecureNoteView().takeIf { cipherType == CipherType.SECURE_NOTE },
|
||||
edit = true,
|
||||
|
||||
+14
@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||
import com.bitwarden.vault.Attachment
|
||||
import com.bitwarden.vault.Card
|
||||
import com.bitwarden.vault.Cipher
|
||||
import com.bitwarden.vault.CipherPermissions
|
||||
import com.bitwarden.vault.CipherRepromptType
|
||||
import com.bitwarden.vault.CipherType
|
||||
import com.bitwarden.vault.Field
|
||||
@@ -53,6 +54,7 @@ fun createMockSdkCipher(number: Int, clock: Clock = FIXED_CLOCK): Cipher =
|
||||
sshKey = createMockSdkSshKey(number = number),
|
||||
favorite = false,
|
||||
passwordHistory = listOf(createMockSdkPasswordHistory(number = number, clock = clock)),
|
||||
permissions = createMockSdkCipherPermissions(),
|
||||
reprompt = CipherRepromptType.NONE,
|
||||
secureNote = createMockSdkSecureNote(),
|
||||
edit = false,
|
||||
@@ -78,6 +80,18 @@ fun createMockSdkPasswordHistory(number: Int, clock: Clock): PasswordHistory =
|
||||
lastUsedDate = clock.instant(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [CipherPermissions] with a given [delete] and [restore] permissions.
|
||||
*/
|
||||
fun createMockSdkCipherPermissions(
|
||||
delete: Boolean = true,
|
||||
restore: Boolean = true,
|
||||
): CipherPermissions =
|
||||
CipherPermissions(
|
||||
delete = delete,
|
||||
restore = restore,
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [Identity] with a given [number].
|
||||
*/
|
||||
|
||||
+2
@@ -135,6 +135,7 @@ private val DEFAULT_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf
|
||||
FlagKey.ChromeAutofill to true,
|
||||
FlagKey.MobileErrorReporting to true,
|
||||
FlagKey.FlightRecorder to true,
|
||||
FlagKey.RestrictCipherItemDeletion to true,
|
||||
FlagKey.PreAuthSettings to true,
|
||||
)
|
||||
|
||||
@@ -154,6 +155,7 @@ private val UPDATED_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf
|
||||
FlagKey.ChromeAutofill to false,
|
||||
FlagKey.MobileErrorReporting to false,
|
||||
FlagKey.FlightRecorder to false,
|
||||
FlagKey.RestrictCipherItemDeletion to false,
|
||||
FlagKey.PreAuthSettings to false,
|
||||
)
|
||||
|
||||
|
||||
+159
-4
@@ -38,6 +38,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.UserVerificationRequirement
|
||||
import com.x8bit.bitwarden.data.autofill.fido2.model.createMockFido2CreateCredentialRequest
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
@@ -46,6 +47,7 @@ import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardMan
|
||||
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.CoachMarkTourType
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
@@ -56,6 +58,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createEditCollectionV
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createEditExceptPasswordsCollectionView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createManageCollectionView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipherPermissions
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFido2CredentialList
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createViewCollectionView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createViewExceptPasswordsCollectionView
|
||||
@@ -190,6 +193,10 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
every { shouldShowAddLoginCoachMarkFlow } returns mutableShouldShowAddLoginCoachMarkFlow
|
||||
}
|
||||
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every { getFeatureFlag(key = FlagKey.RestrictCipherItemDeletion) } returns false
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic(CipherView::toViewState)
|
||||
@@ -1146,10 +1153,10 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
.copy(shouldExitOnSave = true)
|
||||
|
||||
val mockProviderCreateCredentialRequest: ProviderCreateCredentialRequest =
|
||||
mockk<ProviderCreateCredentialRequest>(relaxed = true) {
|
||||
every { callingAppInfo } returns mockk(relaxed = true)
|
||||
every { callingRequest } returns mockk<CreatePasswordRequest>(relaxed = true)
|
||||
}
|
||||
mockk<ProviderCreateCredentialRequest>(relaxed = true) {
|
||||
every { callingAppInfo } returns mockk(relaxed = true)
|
||||
every { callingRequest } returns mockk<CreatePasswordRequest>(relaxed = true)
|
||||
}
|
||||
|
||||
every {
|
||||
ProviderCreateCredentialRequest.fromBundle(any())
|
||||
@@ -1304,6 +1311,152 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in edit mode, canDelete should be false when cipher permission is false`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(1)
|
||||
.copy(
|
||||
permissions = createMockSdkCipherPermissions(
|
||||
delete = false,
|
||||
restore = false,
|
||||
),
|
||||
)
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val stateWithName = createVaultAddItemState(
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
originalCipher = cipherView,
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.HiddenField(
|
||||
itemId = "testId",
|
||||
name = "mockName-1",
|
||||
value = "mockValue-1",
|
||||
),
|
||||
),
|
||||
notes = "mockNotes-1",
|
||||
canDelete = false,
|
||||
),
|
||||
)
|
||||
every {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.RestrictCipherItemDeletion)
|
||||
} returns true
|
||||
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
isIndividualVaultDisabled = false,
|
||||
totpData = null,
|
||||
resourceManager = resourceManager,
|
||||
clock = fixedClock,
|
||||
canDelete = false,
|
||||
canAssignToCollections = true,
|
||||
)
|
||||
} returns stateWithName.viewState
|
||||
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
data = createVaultData(
|
||||
cipherView = cipherView,
|
||||
collectionViewList = listOf(
|
||||
createEditCollectionView(number = 1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = stateWithName,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
vaultItemCipherType = VaultItemCipherType.LOGIN,
|
||||
),
|
||||
)
|
||||
|
||||
verify {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
isIndividualVaultDisabled = false,
|
||||
totpData = null,
|
||||
resourceManager = resourceManager,
|
||||
clock = fixedClock,
|
||||
canDelete = false,
|
||||
canAssignToCollections = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in edit mode, canDelete should be true when cipher permission is true`() =
|
||||
runTest {
|
||||
val cipherView = createMockCipherView(1)
|
||||
.copy(
|
||||
permissions = createMockSdkCipherPermissions(
|
||||
delete = true,
|
||||
restore = false,
|
||||
),
|
||||
)
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val stateWithName = createVaultAddItemState(
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
originalCipher = cipherView,
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.HiddenField(
|
||||
itemId = "testId",
|
||||
name = "mockName-1",
|
||||
value = "mockValue-1",
|
||||
),
|
||||
),
|
||||
notes = "mockNotes-1",
|
||||
canDelete = true,
|
||||
),
|
||||
)
|
||||
every {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.RestrictCipherItemDeletion)
|
||||
} returns true
|
||||
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
isIndividualVaultDisabled = false,
|
||||
totpData = null,
|
||||
resourceManager = resourceManager,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
)
|
||||
} returns stateWithName.viewState
|
||||
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
data = createVaultData(
|
||||
cipherView = cipherView,
|
||||
collectionViewList = listOf(
|
||||
createEditCollectionView(number = 1),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = stateWithName,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
vaultItemCipherType = VaultItemCipherType.LOGIN,
|
||||
),
|
||||
)
|
||||
|
||||
verify {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
isIndividualVaultDisabled = false,
|
||||
totpData = null,
|
||||
resourceManager = resourceManager,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in edit mode, canDelete should be false when cipher is in a collection the user cannot manage`() =
|
||||
@@ -3243,6 +3396,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
organizationEventManager = organizationEventManager,
|
||||
networkConnectionManager = networkConnectionManager,
|
||||
firstTimeActionManager = firstTimeActionManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4582,6 +4736,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
||||
organizationEventManager = organizationEventManager,
|
||||
networkConnectionManager = networkConnectionManager,
|
||||
firstTimeActionManager = firstTimeActionManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
||||
private fun createVaultData(
|
||||
|
||||
+2
@@ -25,6 +25,7 @@ import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
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
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipherPermissions
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFido2CredentialList
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
@@ -636,6 +637,7 @@ private val DEFAULT_BASE_CIPHER_VIEW: CipherView = CipherView(
|
||||
lastUsedDate = FIXED_CLOCK.instant(),
|
||||
),
|
||||
),
|
||||
permissions = createMockSdkCipherPermissions(),
|
||||
creationDate = FIXED_CLOCK.instant(),
|
||||
deletedDate = null,
|
||||
revisionDate = FIXED_CLOCK.instant(),
|
||||
|
||||
@@ -3190,6 +3190,7 @@ private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common =
|
||||
),
|
||||
),
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
favorite = false,
|
||||
@@ -3278,6 +3279,7 @@ private val EMPTY_COMMON: VaultItemState.ViewState.Content.Common =
|
||||
requiresCloneConfirmation = false,
|
||||
attachments = emptyList(),
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
favorite = false,
|
||||
|
||||
+227
@@ -20,9 +20,11 @@ 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.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
@@ -30,6 +32,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentReposito
|
||||
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
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipherPermissions
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
@@ -102,6 +105,8 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
every { edit } returns true
|
||||
every { folderId } returns null
|
||||
every { organizationId } returns null
|
||||
every { permissions } returns null
|
||||
every { deletedDate } returns null
|
||||
}
|
||||
private val mockEnvironmentRepository = FakeEnvironmentRepository()
|
||||
private val mutableIsIconLoadingDisabledFlow = MutableStateFlow(false)
|
||||
@@ -109,6 +114,9 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
every { isIconLoadingDisabled } returns false
|
||||
every { isIconLoadingDisabledFlow } returns mutableIsIconLoadingDisabledFlow
|
||||
}
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every { getFeatureFlag(key = FlagKey.RestrictCipherItemDeletion) } returns false
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
@@ -198,6 +206,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -228,6 +237,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -251,6 +261,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -300,6 +311,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -338,6 +350,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -387,6 +400,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -441,6 +455,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -485,6 +500,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -524,6 +540,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -567,6 +584,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -613,6 +631,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -670,6 +689,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -708,6 +728,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -731,6 +752,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -762,6 +784,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -831,6 +854,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -891,6 +915,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -963,6 +988,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -993,6 +1019,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1013,6 +1040,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1037,6 +1065,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1082,6 +1111,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1120,6 +1150,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1160,6 +1191,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -1197,6 +1229,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1222,6 +1255,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1252,6 +1286,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1276,6 +1311,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1316,6 +1352,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1348,6 +1385,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1376,6 +1414,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1430,6 +1469,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1460,6 +1500,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1484,6 +1525,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1522,6 +1564,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1552,6 +1595,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1576,6 +1620,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1624,6 +1669,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1686,6 +1732,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1758,6 +1805,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = null,
|
||||
@@ -1931,6 +1979,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -1976,6 +2025,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2022,6 +2072,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2045,6 +2096,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2078,6 +2130,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2098,6 +2151,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2124,6 +2178,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2180,6 +2235,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
@@ -2207,6 +2263,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2251,6 +2308,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2282,6 +2340,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2302,6 +2361,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2335,6 +2395,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2356,6 +2417,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2391,6 +2453,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2415,6 +2478,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2453,6 +2517,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = createTotpCodeData(),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2492,6 +2557,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2524,6 +2590,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2544,6 +2611,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2572,6 +2640,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2592,6 +2661,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2624,6 +2694,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2644,6 +2715,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2675,6 +2747,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2695,6 +2768,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2727,6 +2801,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2747,6 +2822,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2775,6 +2851,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2795,6 +2872,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2827,6 +2905,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2846,6 +2925,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2877,6 +2957,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2909,6 +2990,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2947,6 +3029,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -2981,6 +3064,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3002,6 +3086,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3035,6 +3120,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3055,6 +3141,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3091,6 +3178,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3132,6 +3220,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3173,6 +3262,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3328,6 +3418,138 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(
|
||||
VaultItemLocation.Organization("mockOrganizationName"),
|
||||
VaultItemLocation.Collection("mockName-1"),
|
||||
VaultItemLocation.Folder("mockName-1"),
|
||||
),
|
||||
)
|
||||
} returns viewState
|
||||
mutableUserStateFlow.value = DEFAULT_USER_STATE.copy(
|
||||
accounts = listOf(
|
||||
DEFAULT_USER_ACCOUNT.copy(
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = "mockOrganizationId",
|
||||
name = "mockOrganizationName",
|
||||
shouldManageResetPassword = false,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.OWNER,
|
||||
keyConnectorUrl = null,
|
||||
userIsClaimedByOrganization = true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel(state = null)
|
||||
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(
|
||||
listOf(createMockCollectionView(number = 1)),
|
||||
)
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(
|
||||
listOf(createMockFolderView(number = 1)),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(viewState = viewState),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `on VaultDataReceive with Loaded and nonnull false permission data should update the ViewState with cipher permissions`() {
|
||||
val viewState = mockk<VaultItemState.ViewState>()
|
||||
every {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.RestrictCipherItemDeletion)
|
||||
} returns true
|
||||
every { mockCipherView.organizationId } returns "mockOrganizationId"
|
||||
every { mockCipherView.collectionIds } returns listOf("mockId-1")
|
||||
every { mockCipherView.folderId } returns "mockId-1"
|
||||
every {
|
||||
mockCipherView.permissions
|
||||
} returns createMockSdkCipherPermissions(delete = false, restore = false)
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = false,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
isIconLoadingDisabled = false,
|
||||
relatedLocations = persistentListOf(
|
||||
VaultItemLocation.Organization("mockOrganizationName"),
|
||||
VaultItemLocation.Collection("mockName-1"),
|
||||
VaultItemLocation.Folder("mockName-1"),
|
||||
),
|
||||
)
|
||||
} returns viewState
|
||||
mutableUserStateFlow.value = DEFAULT_USER_STATE.copy(
|
||||
accounts = listOf(
|
||||
DEFAULT_USER_ACCOUNT.copy(
|
||||
organizations = listOf(
|
||||
Organization(
|
||||
id = "mockOrganizationId",
|
||||
name = "mockOrganizationName",
|
||||
shouldManageResetPassword = false,
|
||||
shouldUseKeyConnector = false,
|
||||
role = OrganizationType.OWNER,
|
||||
keyConnectorUrl = null,
|
||||
userIsClaimedByOrganization = true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val viewModel = createViewModel(state = null)
|
||||
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(
|
||||
listOf(createMockCollectionView(number = 1)),
|
||||
)
|
||||
mutableFoldersStateFlow.value = DataState.Loaded(
|
||||
listOf(createMockFolderView(number = 1)),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(viewState = viewState),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `on VaultDataReceive with Loaded and nonnull true permission data should update the ViewState with cipher permissions`() {
|
||||
val viewState = mockk<VaultItemState.ViewState>()
|
||||
every {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.RestrictCipherItemDeletion)
|
||||
} returns true
|
||||
every { mockCipherView.organizationId } returns "mockOrganizationId"
|
||||
every { mockCipherView.deletedDate } returns Instant.MIN
|
||||
every { mockCipherView.collectionIds } returns listOf("mockId-1")
|
||||
every { mockCipherView.folderId } returns "mockId-1"
|
||||
every { mockCipherView.permissions } returns createMockSdkCipherPermissions()
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3401,6 +3623,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3446,6 +3669,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3486,6 +3710,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||
@@ -3568,6 +3793,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
organizationEventManager = eventManager,
|
||||
environmentRepository = environmentRepository,
|
||||
settingsRepository = settingsRepository,
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
||||
private fun createViewState(
|
||||
@@ -3748,6 +3974,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
||||
),
|
||||
),
|
||||
canDelete = true,
|
||||
canRestore = false,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
favorite = false,
|
||||
|
||||
+15
@@ -68,6 +68,7 @@ class CipherViewExtensionsTest {
|
||||
),
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -109,6 +110,7 @@ class CipherViewExtensionsTest {
|
||||
),
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -143,6 +145,7 @@ class CipherViewExtensionsTest {
|
||||
),
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -177,6 +180,7 @@ class CipherViewExtensionsTest {
|
||||
),
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -217,6 +221,7 @@ class CipherViewExtensionsTest {
|
||||
),
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -247,6 +252,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -275,6 +281,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -306,6 +313,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -347,6 +355,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -393,6 +402,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -441,6 +451,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -473,6 +484,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -503,6 +515,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -550,6 +563,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
@@ -577,6 +591,7 @@ class CipherViewExtensionsTest {
|
||||
totpCodeItemData = null,
|
||||
clock = fixedClock,
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
baseIconUrl = "https://example.com/",
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.bitwarden.vault.SshKeyView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFido2CredentialList
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipherPermissions
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.model.TotpCodeItemData
|
||||
@@ -153,6 +154,7 @@ fun createCipherView(type: CipherType, isEmpty: Boolean): CipherView =
|
||||
),
|
||||
)
|
||||
.takeUnless { isEmpty },
|
||||
permissions = createMockSdkCipherPermissions(),
|
||||
creationDate = Instant.ofEpochSecond(1_000L),
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.ofEpochSecond(1_000L),
|
||||
@@ -174,6 +176,7 @@ fun createCommonContent(
|
||||
requiresCloneConfirmation = false,
|
||||
attachments = emptyList(),
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
favorite = false,
|
||||
@@ -236,6 +239,7 @@ fun createCommonContent(
|
||||
),
|
||||
),
|
||||
canDelete = true,
|
||||
canRestore = true,
|
||||
canAssignToCollections = true,
|
||||
canEdit = true,
|
||||
favorite = false,
|
||||
|
||||
+6
@@ -110,6 +110,7 @@ class VaultAddItemStateExtensionsTest {
|
||||
attachments = null,
|
||||
fields = emptyList(),
|
||||
passwordHistory = null,
|
||||
permissions = null,
|
||||
creationDate = Instant.MIN,
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.MIN,
|
||||
@@ -294,6 +295,7 @@ class VaultAddItemStateExtensionsTest {
|
||||
),
|
||||
),
|
||||
passwordHistory = null,
|
||||
permissions = null,
|
||||
creationDate = Instant.MIN,
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.MIN,
|
||||
@@ -426,6 +428,7 @@ class VaultAddItemStateExtensionsTest {
|
||||
attachments = null,
|
||||
fields = emptyList(),
|
||||
passwordHistory = null,
|
||||
permissions = null,
|
||||
creationDate = Instant.MIN,
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.MIN,
|
||||
@@ -612,6 +615,7 @@ class VaultAddItemStateExtensionsTest {
|
||||
attachments = null,
|
||||
fields = emptyList(),
|
||||
passwordHistory = null,
|
||||
permissions = null,
|
||||
creationDate = Instant.MIN,
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.MIN,
|
||||
@@ -754,6 +758,7 @@ class VaultAddItemStateExtensionsTest {
|
||||
attachments = null,
|
||||
fields = emptyList(),
|
||||
passwordHistory = null,
|
||||
permissions = null,
|
||||
creationDate = Instant.MIN,
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.MIN,
|
||||
@@ -972,6 +977,7 @@ private val DEFAULT_BASE_CIPHER_VIEW: CipherView = CipherView(
|
||||
lastUsedDate = Instant.MIN,
|
||||
),
|
||||
),
|
||||
permissions = null,
|
||||
creationDate = Instant.MIN,
|
||||
deletedDate = null,
|
||||
revisionDate = Instant.MIN,
|
||||
|
||||
Reference in New Issue
Block a user