mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 09:56:45 -06:00
[PM-27118] Restrict Credential Exchange import based on Personal Ownership policy (#6220)
This commit is contained in:
parent
1904c4ffb9
commit
e1bb3a4b5d
@ -2,14 +2,17 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.network.model.PolicyTypeJson
|
||||
import com.bitwarden.ui.platform.base.BackgroundEvent
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
|
||||
import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
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.ui.platform.model.SnackbarRelay
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -25,6 +28,7 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
snackbarRelayManager: SnackbarRelayManager<SnackbarRelay>,
|
||||
private val firstTimeActionManager: FirstTimeActionManager,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
private val policyManager: PolicyManager,
|
||||
) : BaseViewModel<VaultSettingsState, VaultSettingsEvent, VaultSettingsAction>(
|
||||
initialState = run {
|
||||
val firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState
|
||||
@ -55,7 +59,13 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
|
||||
featureFlagManager
|
||||
.getFeatureFlagFlow(key = FlagKey.CredentialExchangeProtocolImport)
|
||||
.map { VaultSettingsAction.Internal.ImportFeatureUpdated(it) }
|
||||
.combine(
|
||||
policyManager.getActivePoliciesFlow(type = PolicyTypeJson.PERSONAL_OWNERSHIP),
|
||||
) { isEnabled, policies ->
|
||||
VaultSettingsAction.Internal.CredentialExchangeAvailabilityChanged(
|
||||
isEnabled = isEnabled && policies.isEmpty(),
|
||||
)
|
||||
}
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
@ -80,8 +90,8 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
handleSnackbarDataReceived(action)
|
||||
}
|
||||
|
||||
is VaultSettingsAction.Internal.ImportFeatureUpdated -> {
|
||||
handleImportFeatureUpdated(action)
|
||||
is VaultSettingsAction.Internal.CredentialExchangeAvailabilityChanged -> {
|
||||
handleCredentialExchangeAvailabilityChanged(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,8 +102,8 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
sendEvent(VaultSettingsEvent.ShowSnackbar(action.data))
|
||||
}
|
||||
|
||||
private fun handleImportFeatureUpdated(
|
||||
action: VaultSettingsAction.Internal.ImportFeatureUpdated,
|
||||
private fun handleCredentialExchangeAvailabilityChanged(
|
||||
action: VaultSettingsAction.Internal.CredentialExchangeAvailabilityChanged,
|
||||
) {
|
||||
mutableStateFlow.update { it.copy(showImportItemsChevron = action.isEnabled) }
|
||||
}
|
||||
@ -128,7 +138,9 @@ class VaultSettingsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleImportItemsClicked() {
|
||||
if (featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport)) {
|
||||
if (featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport) &&
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP).isEmpty()
|
||||
) {
|
||||
sendEvent(VaultSettingsEvent.NavigateToImportItems)
|
||||
} else {
|
||||
sendEvent(VaultSettingsEvent.NavigateToImportVault)
|
||||
@ -218,9 +230,9 @@ sealed class VaultSettingsAction {
|
||||
*/
|
||||
sealed class Internal : VaultSettingsAction() {
|
||||
/**
|
||||
* Indicates that the import feature flag has been updated.
|
||||
* Indicates that the CXF import feature availability has changed.
|
||||
*/
|
||||
data class ImportFeatureUpdated(
|
||||
data class CredentialExchangeAvailabilityChanged(
|
||||
val isEnabled: Boolean,
|
||||
) : Internal()
|
||||
|
||||
|
||||
@ -3,12 +3,15 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.network.model.PolicyTypeJson
|
||||
import com.bitwarden.network.model.SyncResponseJson
|
||||
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
|
||||
import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
import com.bitwarden.ui.util.asText
|
||||
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.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.ui.platform.model.SnackbarRelay
|
||||
import io.mockk.every
|
||||
@ -38,6 +41,11 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
getFeatureFlagFlow(FlagKey.CredentialExchangeProtocolImport)
|
||||
} returns mutableFeatureFlagFlow
|
||||
}
|
||||
private val mutablePoliciesFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Policy>>()
|
||||
private val policyManager = mockk<PolicyManager> {
|
||||
every { getActivePolicies(any()) } returns emptyList()
|
||||
every { getActivePoliciesFlow(any()) } returns mutablePoliciesFlow
|
||||
}
|
||||
|
||||
private val mutableSnackbarSharedFlow = bufferedMutableSharedFlow<BitwardenSnackbarData>()
|
||||
private val snackbarRelayManager = mockk<SnackbarRelayManager<SnackbarRelay>> {
|
||||
@ -84,6 +92,25 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ImportItemsClick should emit NavigateToImportVault when policy is not empty`() = runTest {
|
||||
every {
|
||||
featureFlagManager.getFeatureFlag(FlagKey.CredentialExchangeProtocolImport)
|
||||
} returns true
|
||||
every {
|
||||
policyManager.getActivePolicies(PolicyTypeJson.PERSONAL_OWNERSHIP)
|
||||
} returns listOf(mockk())
|
||||
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||
assertEquals(
|
||||
VaultSettingsEvent.NavigateToImportVault,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `ImportItemsClick should emit NavigateToImportItems when CredentialExchangeProtocolImport is enabled`() =
|
||||
@ -160,21 +187,36 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CredentialExchangeProtocolImport flag changes should update state accordingly`() {
|
||||
fun `showImportItemsChevron should display based on feature flag and policies`() {
|
||||
val viewModel = createViewModel()
|
||||
// Verify chevron is shown when feature flag is enabled and no policies (default state)
|
||||
assertEquals(
|
||||
viewModel.stateFlow.value,
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
|
||||
)
|
||||
|
||||
// Verify chevron is hidden when feature flag is disabled and no policies
|
||||
mutableFeatureFlagFlow.tryEmit(false)
|
||||
mutablePoliciesFlow.tryEmit(emptyList())
|
||||
assertEquals(
|
||||
viewModel.stateFlow.value,
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
|
||||
)
|
||||
|
||||
// Verify chevron is hidden when feature flag is enabled and policies exist
|
||||
mutableFeatureFlagFlow.tryEmit(true)
|
||||
mutablePoliciesFlow.tryEmit(listOf(mockk()))
|
||||
assertEquals(
|
||||
viewModel.stateFlow.value,
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = true),
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
|
||||
)
|
||||
|
||||
// Verify chevron is hidden when feature flag is disabled and no policies
|
||||
mutableFeatureFlagFlow.tryEmit(false)
|
||||
mutablePoliciesFlow.tryEmit(emptyList())
|
||||
assertEquals(
|
||||
viewModel.stateFlow.value,
|
||||
VaultSettingsState(showImportActionCard = true, showImportItemsChevron = false),
|
||||
)
|
||||
}
|
||||
|
||||
@ -182,6 +224,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||
firstTimeActionManager = firstTimeActionManager,
|
||||
snackbarRelayManager = snackbarRelayManager,
|
||||
featureFlagManager = featureFlagManager,
|
||||
policyManager = policyManager,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user