mirror of
https://github.com/bitwarden/android.git
synced 2026-06-06 22:42:58 -05:00
BIT-782: Implement Personal Ownership policy support (#920)
This commit is contained in:
committed by
Álison Fernandes
parent
f380e21600
commit
debfbc04b0
@@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
|
||||
@@ -17,6 +18,7 @@ import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
@@ -61,6 +63,7 @@ class SearchViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val clock: Clock,
|
||||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val policyManager: PolicyManager,
|
||||
private val autofillSelectionManager: AutofillSelectionManager,
|
||||
private val vaultRepo: VaultRepository,
|
||||
private val authRepo: AuthRepository,
|
||||
@@ -84,7 +87,11 @@ class SearchViewModel @Inject constructor(
|
||||
dialogState = null,
|
||||
vaultFilterData = when (searchType) {
|
||||
is SearchType.Sends -> null
|
||||
is SearchType.Vault -> userState.activeAccount.toVaultFilterData()
|
||||
is SearchType.Vault -> userState.activeAccount.toVaultFilterData(
|
||||
isIndividualVaultDisabled = policyManager
|
||||
.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
.any(),
|
||||
)
|
||||
},
|
||||
baseWebSendUrl = environmentRepo.environment.environmentUrlData.baseWebSendUrl,
|
||||
baseIconUrl = environmentRepo.environment.environmentUrlData.baseIconUrl,
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenPolicyWarningText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCardTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
|
||||
@@ -58,6 +59,17 @@ fun VaultAddEditContent(
|
||||
modifier = modifier
|
||||
.semantics { testTagsAsResourceId = true },
|
||||
) {
|
||||
item {
|
||||
if (state.isIndividualVaultDisabled && isAddItemMode) {
|
||||
BitwardenPolicyWarningText(
|
||||
text = stringResource(R.string.personal_ownership_policy_in_effect),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull
|
||||
@@ -17,6 +18,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratorResult
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteCipherResult
|
||||
@@ -74,6 +76,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val authRepository: AuthRepository,
|
||||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val policyManager: PolicyManager,
|
||||
private val vaultRepository: VaultRepository,
|
||||
private val generatorRepository: GeneratorRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
@@ -84,6 +87,9 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
?: run {
|
||||
val vaultAddEditType = VaultAddEditArgs(savedStateHandle).vaultAddEditType
|
||||
val isIndividualVaultDisabled = policyManager
|
||||
.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
.any()
|
||||
|
||||
// Check for autofill data to pre-populate
|
||||
val autofillSaveItem = specialCircumstanceManager
|
||||
@@ -104,11 +110,12 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
val defaultAddTypeContent = autofillSelectionData
|
||||
?.toDefaultAddTypeContent()
|
||||
?.toDefaultAddTypeContent(isIndividualVaultDisabled)
|
||||
?: autofillSaveItem
|
||||
?.toDefaultAddTypeContent()
|
||||
?.toDefaultAddTypeContent(isIndividualVaultDisabled)
|
||||
?: VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
|
||||
)
|
||||
|
||||
@@ -1140,8 +1147,11 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
private fun VaultAddEditState.determineContentState(
|
||||
vaultData: VaultData,
|
||||
userData: UserState?,
|
||||
): VaultAddEditState =
|
||||
copy(
|
||||
): VaultAddEditState {
|
||||
val isIndividualVaultDisabled = policyManager
|
||||
.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
.any()
|
||||
return copy(
|
||||
viewState = vaultData.cipherViewList
|
||||
.find { it.id == vaultAddEditType.vaultItemId }
|
||||
.validateCipherOrReturnErrorState(
|
||||
@@ -1152,6 +1162,7 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
// or use the current state for Add
|
||||
(cipherView?.toViewState(
|
||||
isClone = isCloneMode,
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
resourceManager = resourceManager,
|
||||
) ?: viewState)
|
||||
.appendFolderAndOwnerData(
|
||||
@@ -1159,10 +1170,12 @@ class VaultAddEditViewModel @Inject constructor(
|
||||
collectionViewList = vaultData.collectionViewList
|
||||
.filter { !it.readOnly },
|
||||
activeAccount = currentAccount,
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleVaultTotpCodeReceive(action: VaultAddEditAction.Internal.TotpCodeReceive) {
|
||||
when (action.totpResult) {
|
||||
@@ -1473,6 +1486,7 @@ data class VaultAddEditState(
|
||||
data class Content(
|
||||
val common: Common,
|
||||
val type: ItemType,
|
||||
val isIndividualVaultDisabled: Boolean,
|
||||
val previousItemTypes: Map<ItemTypeOption, ItemType> = emptyMap(),
|
||||
) : ViewState() {
|
||||
|
||||
@@ -1512,6 +1526,7 @@ data class VaultAddEditState(
|
||||
*/
|
||||
val selectedOwner: Owner?
|
||||
get() = availableOwners.find { it.id == selectedOwnerId }
|
||||
?: availableOwners.firstOrNull()
|
||||
|
||||
/**
|
||||
* Helper to provide the currently selected folder.
|
||||
|
||||
@@ -11,11 +11,14 @@ import java.util.UUID
|
||||
* Returns pre-filled content that may be used for an "add" type
|
||||
* [VaultAddEditState.ViewState.Content].
|
||||
*/
|
||||
fun AutofillSaveItem.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content =
|
||||
fun AutofillSaveItem.toDefaultAddTypeContent(
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
): VaultAddEditState.ViewState.Content =
|
||||
when (this) {
|
||||
is AutofillSaveItem.Card -> {
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Card(
|
||||
number = this.number.orEmpty(),
|
||||
expirationMonth = VaultCardExpirationMonth
|
||||
@@ -35,6 +38,7 @@ fun AutofillSaveItem.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Cont
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = simpleUri.orEmpty(),
|
||||
),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(
|
||||
username = this.username.orEmpty(),
|
||||
password = this.password.orEmpty(),
|
||||
|
||||
@@ -10,7 +10,9 @@ import java.util.UUID
|
||||
* Returns pre-filled content that may be used for an "add" type
|
||||
* [VaultAddEditState.ViewState.Content].
|
||||
*/
|
||||
fun AutofillSelectionData.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content {
|
||||
fun AutofillSelectionData.toDefaultAddTypeContent(
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
): VaultAddEditState.ViewState.Content {
|
||||
val uri = this.uri
|
||||
val simpleUri = uri?.toHostOrPathOrNull()
|
||||
val defaultAddType = when (this.type) {
|
||||
@@ -34,6 +36,7 @@ fun AutofillSelectionData.toDefaultAddTypeContent(): VaultAddEditState.ViewState
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = simpleUri.orEmpty(),
|
||||
),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
type = defaultAddType,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.UUID
|
||||
*/
|
||||
fun CipherView.toViewState(
|
||||
isClone: Boolean,
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
resourceManager: ResourceManager,
|
||||
): VaultAddEditState.ViewState =
|
||||
VaultAddEditState.ViewState.Content(
|
||||
@@ -86,6 +87,7 @@ fun CipherView.toViewState(
|
||||
availableOwners = emptyList(),
|
||||
customFieldData = this.fields.orEmpty().map { it.toCustomField() },
|
||||
),
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -95,6 +97,7 @@ fun VaultAddEditState.ViewState.appendFolderAndOwnerData(
|
||||
folderViewList: List<FolderView>,
|
||||
collectionViewList: List<CollectionView>,
|
||||
activeAccount: UserState.Account,
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
resourceManager: ResourceManager,
|
||||
): VaultAddEditState.ViewState {
|
||||
return (this as? VaultAddEditState.ViewState.Content)?.let { currentContentState ->
|
||||
@@ -111,6 +114,7 @@ fun VaultAddEditState.ViewState.appendFolderAndOwnerData(
|
||||
),
|
||||
availableOwners = activeAccount.toAvailableOwners(
|
||||
collectionViewList = collectionViewList,
|
||||
isIndividualVaultDisabled = isIndividualVaultDisabled,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -161,10 +165,17 @@ private fun UserState.Account.toSelectedOwnerId(cipherView: CipherView?): String
|
||||
|
||||
private fun UserState.Account.toAvailableOwners(
|
||||
collectionViewList: List<CollectionView>,
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
): List<VaultAddEditState.Owner> =
|
||||
listOf(VaultAddEditState.Owner(name = email, id = null, collections = emptyList()))
|
||||
.plus(
|
||||
organizations.map {
|
||||
listOfNotNull(
|
||||
VaultAddEditState.Owner(
|
||||
name = email,
|
||||
id = null,
|
||||
collections = emptyList(),
|
||||
)
|
||||
.takeUnless { isIndividualVaultDisabled },
|
||||
*organizations
|
||||
.map {
|
||||
VaultAddEditState.Owner(
|
||||
name = it.name.orEmpty(),
|
||||
id = it.id,
|
||||
@@ -181,8 +192,9 @@ private fun UserState.Account.toAvailableOwners(
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
.toTypedArray(),
|
||||
)
|
||||
|
||||
private fun FieldView.toCustomField() =
|
||||
when (this.type) {
|
||||
|
||||
@@ -7,10 +7,12 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseIconUrl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
@@ -52,6 +54,7 @@ class VaultViewModel @Inject constructor(
|
||||
private val authRepository: AuthRepository,
|
||||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val clock: Clock,
|
||||
private val policyManager: PolicyManager,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val vaultRepository: VaultRepository,
|
||||
) : BaseViewModel<VaultState, VaultEvent, VaultAction>(
|
||||
@@ -59,7 +62,11 @@ class VaultViewModel @Inject constructor(
|
||||
val userState = requireNotNull(authRepository.userStateFlow.value)
|
||||
val accountSummaries = userState.toAccountSummaries()
|
||||
val activeAccountSummary = userState.toActiveAccountSummary()
|
||||
val vaultFilterData = userState.activeAccount.toVaultFilterData()
|
||||
val vaultFilterData = userState.activeAccount.toVaultFilterData(
|
||||
isIndividualVaultDisabled = policyManager
|
||||
.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
.any(),
|
||||
)
|
||||
val appBarTitle = vaultFilterData.toAppBarTitle()
|
||||
VaultState(
|
||||
appBarTitle = appBarTitle,
|
||||
@@ -414,7 +421,11 @@ class VaultViewModel @Inject constructor(
|
||||
// navigating.
|
||||
if (state.isSwitchingAccounts) return
|
||||
|
||||
val vaultFilterData = userState.activeAccount.toVaultFilterData()
|
||||
val vaultFilterData = userState.activeAccount.toVaultFilterData(
|
||||
isIndividualVaultDisabled = policyManager
|
||||
.getActivePolicies(type = PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
.any(),
|
||||
)
|
||||
val appBarTitle = vaultFilterData.toAppBarTitle()
|
||||
mutableStateFlow.update {
|
||||
val accountSummaries = userState.toAccountSummaries()
|
||||
|
||||
@@ -47,16 +47,18 @@ fun UserState.Account.toAccountSummary(
|
||||
* Converts the given [UserState.Account] to a [VaultFilterData] (if applicable). Filter data is
|
||||
* only relevant when the given account is associated with one or more organizations.
|
||||
*/
|
||||
fun UserState.Account.toVaultFilterData(): VaultFilterData? =
|
||||
fun UserState.Account.toVaultFilterData(
|
||||
isIndividualVaultDisabled: Boolean,
|
||||
): VaultFilterData? =
|
||||
this
|
||||
.organizations
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { organizations ->
|
||||
VaultFilterData(
|
||||
selectedVaultFilterType = VaultFilterType.AllVaults,
|
||||
vaultFilterTypes = listOf(
|
||||
vaultFilterTypes = listOfNotNull(
|
||||
VaultFilterType.AllVaults,
|
||||
VaultFilterType.MyVault,
|
||||
VaultFilterType.MyVault.takeUnless { isIndividualVaultDisabled },
|
||||
*organizations
|
||||
.sortedBy { it.name }
|
||||
.map { organization ->
|
||||
|
||||
Reference in New Issue
Block a user