mirror of
https://github.com/bitwarden/android.git
synced 2026-11-10 01:15:13 -06:00
[PM-40528] feat: Wire SendControls precedence into DisableSend/SendOptions enforcement points (#7239)
This commit is contained in:
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.policies.PolicyView
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.EffectiveSendPolicy
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
@@ -18,6 +19,19 @@ interface PolicyManager {
|
||||
*/
|
||||
fun getActivePolicies(type: PolicyType): List<PolicyView>
|
||||
|
||||
/**
|
||||
* Returns the current, precedence-resolved [EffectiveSendPolicy] for the active user. When
|
||||
* the `pm-31885-send-controls` feature flag is disabled, or no organization has an active
|
||||
* SendControls policy, this is equivalent to today's DisableSend/SendOptions logic.
|
||||
*/
|
||||
fun getEffectiveSendPolicy(): EffectiveSendPolicy
|
||||
|
||||
/**
|
||||
* Returns a flow that emits the current [EffectiveSendPolicy] for the active user whenever
|
||||
* the underlying policies or the `pm-31885-send-controls` feature flag change.
|
||||
*/
|
||||
fun getEffectiveSendPolicyFlow(): Flow<EffectiveSendPolicy>
|
||||
|
||||
/**
|
||||
* Get all the policies of the given [type] that are enabled and applicable to the [userId].
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,10 @@ import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.policies.PolicyView
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.policyInformation
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toSdkOrganizationPolicyContext
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toSdkPolicyViews
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -47,6 +50,30 @@ class PolicyManagerImpl(
|
||||
?.let { userId -> getUserPolicies(userId = userId, type = type) }
|
||||
.orEmpty()
|
||||
|
||||
override fun getEffectiveSendPolicy(): EffectiveSendPolicy =
|
||||
resolveEffectiveSendPolicy(
|
||||
disableSendPolicies = getActivePolicies(type = PolicyType.DISABLE_SEND),
|
||||
isSendControlsEnabled = featureFlagManager.getFeatureFlag(key = FlagKey.SendControls),
|
||||
sendControlsPolicies = getActivePolicies(type = PolicyType.SEND_CONTROLS),
|
||||
sendOptionsPolicies = getActivePolicies(type = PolicyType.SEND_OPTIONS),
|
||||
)
|
||||
|
||||
override fun getEffectiveSendPolicyFlow(): Flow<EffectiveSendPolicy> =
|
||||
combine(
|
||||
featureFlagManager.getFeatureFlagFlow(key = FlagKey.SendControls),
|
||||
getActivePoliciesFlow(type = PolicyType.SEND_CONTROLS),
|
||||
getActivePoliciesFlow(type = PolicyType.DISABLE_SEND),
|
||||
getActivePoliciesFlow(type = PolicyType.SEND_OPTIONS),
|
||||
) { isSendControlsEnabled, sendControlsPolicies, disableSendPolicies, sendOptionsPolicies ->
|
||||
resolveEffectiveSendPolicy(
|
||||
disableSendPolicies = disableSendPolicies,
|
||||
isSendControlsEnabled = isSendControlsEnabled,
|
||||
sendControlsPolicies = sendControlsPolicies,
|
||||
sendOptionsPolicies = sendOptionsPolicies,
|
||||
)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
override fun getUserPolicies(
|
||||
userId: String,
|
||||
type: PolicyType,
|
||||
@@ -170,6 +197,62 @@ class PolicyManagerImpl(
|
||||
this.organizationShouldUsePolicies
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the [EffectiveSendPolicy] from the raw, already-filtered active policies of each
|
||||
* relevant type. When [isSendControlsEnabled] is `false`, this is equivalent to the legacy
|
||||
* DisableSend/SendOptions logic. When `true`, an organization's active SendControls policy
|
||||
* (if any) takes precedence over that same organization's legacy DisableSend/SendOptions
|
||||
* policies, while other organizations' legacy policies remain in effect.
|
||||
*/
|
||||
private fun resolveEffectiveSendPolicy(
|
||||
disableSendPolicies: List<PolicyView>,
|
||||
isSendControlsEnabled: Boolean,
|
||||
sendControlsPolicies: List<PolicyView>,
|
||||
sendOptionsPolicies: List<PolicyView>,
|
||||
): EffectiveSendPolicy {
|
||||
if (!isSendControlsEnabled) {
|
||||
return EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = sendOptionsPolicies
|
||||
.mapNotNull { it.policyInformation as? PolicyInformation.SendOptions }
|
||||
.any { it.shouldDisableHideEmail ?: false },
|
||||
disableSend = disableSendPolicies.any(),
|
||||
whoCanAccess = null,
|
||||
)
|
||||
}
|
||||
|
||||
val decodedSendControls = sendControlsPolicies
|
||||
// Sorted so that the passthrough fields below are sourced deterministically when
|
||||
// multiple organizations have an active SendControls policy.
|
||||
.sortedBy { it.revisionDate }
|
||||
.mapNotNull { policy ->
|
||||
(policy.policyInformation as? PolicyInformation.SendControls)
|
||||
?.let { policy.organizationId to it }
|
||||
}
|
||||
val organizationIdsWithSendControls = decodedSendControls.map { it.first }.toSet()
|
||||
|
||||
val remainingDisableSendPolicies = disableSendPolicies
|
||||
.filterNot { organizationIdsWithSendControls.contains(it.organizationId) }
|
||||
val remainingSendOptions = sendOptionsPolicies
|
||||
.filterNot { organizationIdsWithSendControls.contains(it.organizationId) }
|
||||
.mapNotNull { it.policyInformation as? PolicyInformation.SendOptions }
|
||||
|
||||
val firstSendControls = decodedSendControls.firstOrNull()?.second
|
||||
|
||||
return EffectiveSendPolicy(
|
||||
allowedDomains = firstSendControls?.allowedDomains,
|
||||
allowedSendTypes = firstSendControls?.allowedSendTypes,
|
||||
deletionHours = firstSendControls?.deletionHours,
|
||||
disableHideEmail = decodedSendControls.any { it.second.disableHideEmail == true } ||
|
||||
remainingSendOptions.any { it.shouldDisableHideEmail ?: false },
|
||||
disableSend = decodedSendControls.any { it.second.disableSend == true } ||
|
||||
remainingDisableSendPolicies.any(),
|
||||
whoCanAccess = firstSendControls?.whoCanAccess,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class OrganizationPolicyData(
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import com.bitwarden.network.model.SendAccessTypeJson
|
||||
import com.bitwarden.network.model.SendTypeJson
|
||||
|
||||
/**
|
||||
* The effective, precedence-resolved Send policy for the active user, combining the legacy
|
||||
* DisableSend/SendOptions policies with the newer SendControls policy (type 21) per the
|
||||
* `pm-31885-send-controls` feature flag.
|
||||
*
|
||||
* When an organization has an active SendControls policy, that organization's legacy
|
||||
* DisableSend/SendOptions policies are ignored in favor of the SendControls values; other
|
||||
* organizations' legacy policies remain in effect.
|
||||
*
|
||||
* @property allowedDomains The allowed recipient email domains, sourced from whichever
|
||||
* organization's active SendControls policy has the earliest revision date, if any. Currently
|
||||
* unused by the UI.
|
||||
* @property allowedSendTypes The types of Sends that are allowed to be created, sourced the same
|
||||
* way as [allowedDomains]. Currently unused by the UI.
|
||||
* @property deletionHours The enforced number of hours until a Send is deleted, sourced the same
|
||||
* way as [allowedDomains]. Currently unused by the UI.
|
||||
* @property disableHideEmail Whether the ability to hide one's email address on a Send should be
|
||||
* disabled.
|
||||
* @property disableSend Whether the ability to create and edit Sends should be disabled.
|
||||
* @property whoCanAccess The access type Sends are restricted to, sourced the same way as
|
||||
* [allowedDomains]. Currently unused by the UI.
|
||||
*/
|
||||
data class EffectiveSendPolicy(
|
||||
val allowedDomains: String?,
|
||||
val allowedSendTypes: List<SendTypeJson>?,
|
||||
val deletionHours: Int?,
|
||||
val disableHideEmail: Boolean,
|
||||
val disableSend: Boolean,
|
||||
val whoCanAccess: SendAccessTypeJson?,
|
||||
)
|
||||
+3
-6
@@ -2,7 +2,6 @@ package com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.ui.platform.base.DeferredBackgroundEvent
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
@@ -35,9 +34,7 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||
notificationState = VaultUnlockedNavBarNotificationState(
|
||||
settingsTabNotificationCount = firstTimeActionManager.allSettingsBadgeCountFlow.value,
|
||||
),
|
||||
areSendsDisabled = policyManager
|
||||
.getActivePolicies(type = PolicyType.DISABLE_SEND)
|
||||
.any(),
|
||||
areSendsDisabled = policyManager.getEffectiveSendPolicy().disableSend,
|
||||
),
|
||||
) {
|
||||
init {
|
||||
@@ -56,8 +53,8 @@ class VaultUnlockedNavBarViewModel @Inject constructor(
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
policyManager
|
||||
.getActivePoliciesFlow(type = PolicyType.DISABLE_SEND)
|
||||
.map { VaultUnlockedNavBarAction.Internal.SendPolicyUpdateReceive(it.any()) }
|
||||
.getEffectiveSendPolicyFlow()
|
||||
.map { VaultUnlockedNavBarAction.Internal.SendPolicyUpdateReceive(it.disableSend) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.data.repository.util.baseWebSendUrl
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.ui.platform.base.BackgroundEvent
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.bitwarden.ui.platform.components.icon.model.IconData
|
||||
@@ -69,9 +68,7 @@ class SendViewModel @Inject constructor(
|
||||
viewState = SendState.ViewState.Loading,
|
||||
dialogState = null,
|
||||
isPullToRefreshSettingEnabled = settingsRepo.getPullToRefreshEnabledFlow().value,
|
||||
policyDisablesSend = policyManager
|
||||
.getActivePolicies(type = PolicyType.DISABLE_SEND)
|
||||
.any(),
|
||||
policyDisablesSend = policyManager.getEffectiveSendPolicy().disableSend,
|
||||
isRefreshing = false,
|
||||
isPremiumUser = authRepo.userStateFlow.value?.activeAccount?.isPremium == true,
|
||||
isUpgradedToPremiumCardEligible = false,
|
||||
@@ -85,8 +82,8 @@ class SendViewModel @Inject constructor(
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
policyManager
|
||||
.getActivePoliciesFlow(type = PolicyType.DISABLE_SEND)
|
||||
.map { SendAction.Internal.PolicyUpdateReceive(it.any()) }
|
||||
.getEffectiveSendPolicyFlow()
|
||||
.map { SendAction.Internal.PolicyUpdateReceive(it.disableSend) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
vaultRepo
|
||||
|
||||
+24
-12
@@ -8,7 +8,8 @@ import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.takeUntilLoaded
|
||||
import com.bitwarden.data.repository.util.baseWebSendUrl
|
||||
import com.bitwarden.data.repository.util.baseWebVaultUrlOrDefault
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.network.model.SendAccessTypeJson
|
||||
import com.bitwarden.network.model.SendTypeJson
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.ui.platform.base.BackgroundEvent
|
||||
import com.bitwarden.ui.platform.base.BaseViewModel
|
||||
@@ -21,13 +22,12 @@ import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.bitwarden.ui.util.concat
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation
|
||||
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
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.model.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.getActivePolicies
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratorResult
|
||||
@@ -98,6 +98,7 @@ class AddEditSendViewModel @Inject constructor(
|
||||
val args = savedStateHandle.toAddEditSendArgs()
|
||||
val sendType = args.sendType
|
||||
val addEditSendType = args.addEditSendType
|
||||
val effectiveSendPolicy = policyManager.getEffectiveSendPolicy()
|
||||
|
||||
AddEditSendState(
|
||||
sendType = sendType,
|
||||
@@ -114,9 +115,7 @@ class AddEditSendViewModel @Inject constructor(
|
||||
noteInput = "",
|
||||
isHideEmailChecked = false,
|
||||
isDeactivateChecked = false,
|
||||
isHideEmailAddressEnabled = !policyManager
|
||||
.getActivePolicies<PolicyInformation.SendOptions>()
|
||||
.any { it.shouldDisableHideEmail ?: false },
|
||||
isHideEmailAddressEnabled = !effectiveSendPolicy.disableHideEmail,
|
||||
deletionDate = clock
|
||||
.instant()
|
||||
.plus(@Suppress("MagicNumber") 7, ChronoUnit.DAYS),
|
||||
@@ -148,9 +147,11 @@ class AddEditSendViewModel @Inject constructor(
|
||||
},
|
||||
dialogState = null,
|
||||
baseWebSendUrl = environmentRepo.environment.baseWebSendUrl,
|
||||
policyDisablesSend = policyManager
|
||||
.getActivePolicies(type = PolicyType.DISABLE_SEND)
|
||||
.any(),
|
||||
policyDisablesSend = effectiveSendPolicy.disableSend,
|
||||
allowedDomains = effectiveSendPolicy.allowedDomains,
|
||||
allowedSendTypes = effectiveSendPolicy.allowedSendTypes,
|
||||
deletionHours = effectiveSendPolicy.deletionHours,
|
||||
whoCanAccess = effectiveSendPolicy.whoCanAccess,
|
||||
isPremium = authRepo.userStateFlow.value?.activeAccount?.isPremium == true,
|
||||
)
|
||||
},
|
||||
@@ -825,9 +826,7 @@ class AddEditSendViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private val isHideEmailAddressEnabled: Boolean
|
||||
get() = !policyManager
|
||||
.getActivePolicies<PolicyInformation.SendOptions>()
|
||||
.any { it.shouldDisableHideEmail ?: false }
|
||||
get() = !policyManager.getEffectiveSendPolicy().disableHideEmail
|
||||
|
||||
private inline fun onContent(
|
||||
crossinline block: (AddEditSendState.ViewState.Content) -> Unit,
|
||||
@@ -886,6 +885,15 @@ class AddEditSendViewModel @Inject constructor(
|
||||
|
||||
/**
|
||||
* Models state for the add/edit send screen.
|
||||
*
|
||||
* @property allowedDomains The allowed recipient email domains, sourced from
|
||||
* [EffectiveSendPolicy.allowedDomains]. Currently unused by the UI.
|
||||
* @property allowedSendTypes The types of Sends that are allowed to be created, sourced from
|
||||
* [EffectiveSendPolicy.allowedSendTypes]. Currently unused by the UI.
|
||||
* @property deletionHours The enforced Send deletion window in hours, sourced from
|
||||
* [EffectiveSendPolicy.deletionHours]. Currently unused by the UI.
|
||||
* @property whoCanAccess The access type Sends are restricted to, sourced from
|
||||
* [EffectiveSendPolicy.whoCanAccess]. Currently unused by the UI.
|
||||
*/
|
||||
@Parcelize
|
||||
data class AddEditSendState(
|
||||
@@ -897,6 +905,10 @@ data class AddEditSendState(
|
||||
val isShared: Boolean,
|
||||
val baseWebSendUrl: String,
|
||||
val policyDisablesSend: Boolean,
|
||||
val allowedDomains: String?,
|
||||
val allowedSendTypes: List<SendTypeJson>?,
|
||||
val deletionHours: Int?,
|
||||
val whoCanAccess: SendAccessTypeJson?,
|
||||
val isPremium: Boolean,
|
||||
) : Parcelable {
|
||||
|
||||
|
||||
+3
-5
@@ -184,9 +184,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
?.let {
|
||||
VaultItemListingState.DialogState.Loading(BitwardenString.loading.asText())
|
||||
},
|
||||
policyDisablesSend = policyManager
|
||||
.getActivePolicies(type = PolicyType.DISABLE_SEND)
|
||||
.any(),
|
||||
policyDisablesSend = policyManager.getEffectiveSendPolicy().disableSend,
|
||||
restrictItemTypesPolicyOrgIds = persistentListOf(),
|
||||
autofillSelectionData = specialCircumstance?.toAutofillSelectionDataOrNull(),
|
||||
hasMasterPassword = userState.activeAccount.hasMasterPassword,
|
||||
@@ -214,8 +212,8 @@ class VaultItemListingViewModel @Inject constructor(
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
policyManager
|
||||
.getActivePoliciesFlow(type = PolicyType.DISABLE_SEND)
|
||||
.map { VaultItemListingsAction.Internal.PolicyUpdateReceive(it.any()) }
|
||||
.getEffectiveSendPolicyFlow()
|
||||
.map { VaultItemListingsAction.Internal.PolicyUpdateReceive(it.disableSend) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.network.model.OrganizationStatusType
|
||||
import com.bitwarden.network.model.OrganizationType
|
||||
import com.bitwarden.network.model.PolicyTypeJson
|
||||
import com.bitwarden.network.model.SendAccessTypeJson
|
||||
import com.bitwarden.network.model.SyncResponseJson
|
||||
import com.bitwarden.network.model.createMockOrganizationNetwork
|
||||
import com.bitwarden.network.model.createMockPolicy
|
||||
@@ -14,6 +15,7 @@ import com.bitwarden.policies.PolicyView
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockPolicyView
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
@@ -38,6 +40,7 @@ class PolicyManagerTest {
|
||||
}
|
||||
private val authSdkSource: AuthSdkSource = mockk()
|
||||
private val mutablePoliciesInAcceptedStateFlagFlow = MutableStateFlow(true)
|
||||
private val mutableSendControlsFlagFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every {
|
||||
getFeatureFlagFlow(key = FlagKey.PoliciesInAcceptedState)
|
||||
@@ -45,6 +48,12 @@ class PolicyManagerTest {
|
||||
every { getFeatureFlag(key = FlagKey.PoliciesInAcceptedState) } answers {
|
||||
mutablePoliciesInAcceptedStateFlagFlow.value
|
||||
}
|
||||
every {
|
||||
getFeatureFlagFlow(key = FlagKey.SendControls)
|
||||
} returns mutableSendControlsFlagFlow
|
||||
every { getFeatureFlag(key = FlagKey.SendControls) } answers {
|
||||
mutableSendControlsFlagFlow.value
|
||||
}
|
||||
}
|
||||
|
||||
private val policyManager: PolicyManager = PolicyManagerImpl(
|
||||
@@ -724,6 +733,454 @@ class PolicyManagerTest {
|
||||
policyManager.getPersonalOwnershipPolicyOrganizationId(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should mirror legacy DisableSend policy when flag is off`() {
|
||||
setUpSendPolicies(
|
||||
disableSendPolicies = listOf(
|
||||
createMockPolicyView(organizationId = "mockId-1", enabled = true),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = true,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should mirror legacy SendOptions policy when flag is off`() {
|
||||
setUpSendPolicies(
|
||||
sendOptionsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_OPTIONS,
|
||||
data = """{"disableHideEmail":true}""",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = true,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should ignore an active SendControls policy when flag is off`() {
|
||||
setUpSendPolicies(
|
||||
sendControlsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"disableSend":false}""",
|
||||
),
|
||||
),
|
||||
disableSendPolicies = listOf(
|
||||
createMockPolicyView(organizationId = "mockId-1", enabled = true),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = true,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should use SendControls for an org that has both policy types when flag is on`() {
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
setUpSendPolicies(
|
||||
sendControlsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"disableSend":false}""",
|
||||
),
|
||||
),
|
||||
disableSendPolicies = listOf(
|
||||
createMockPolicyView(organizationId = "mockId-1", enabled = true),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should still apply legacy DisableSend for an org without SendControls when flag is on`() {
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
setUpSendPolicies(
|
||||
sendControlsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"disableSend":false}""",
|
||||
),
|
||||
),
|
||||
disableSendPolicies = listOf(
|
||||
// mockId-1 has SendControls (exclusion applies), mockId-2 does not.
|
||||
createMockPolicyView(organizationId = "mockId-1", enabled = true),
|
||||
createMockPolicyView(organizationId = "mockId-2", enabled = true),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = true,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should OR disableHideEmail across SendControls and remaining legacy orgs`() {
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
setUpSendPolicies(
|
||||
sendControlsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"disableHideEmail":false}""",
|
||||
),
|
||||
),
|
||||
sendOptionsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-2",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_OPTIONS,
|
||||
data = """{"disableHideEmail":true}""",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = true,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should source new fields from the earliest active SendControls policy`() {
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
setUpSendPolicies(
|
||||
sendControlsPolicies = listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """
|
||||
{
|
||||
"whoCanAccess":2,
|
||||
"allowedDomains":"bitwarden.com",
|
||||
"deletionHours":24
|
||||
}
|
||||
""".trimIndent(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = "bitwarden.com",
|
||||
allowedSendTypes = null,
|
||||
deletionHours = 24,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = SendAccessTypeJson.SPECIFIC_PEOPLE,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should source new fields by revision date rather than list order`() {
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
setUpSendPolicies(
|
||||
sendControlsPolicies = listOf(
|
||||
// Listed first, but revised later, so it should lose to the policy below.
|
||||
createMockPolicyView(
|
||||
number = 2,
|
||||
organizationId = "mockId-2",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"allowedDomains":"later.example.com"}""",
|
||||
revisionDate = Instant.parse("2026-06-01T00:00:00Z"),
|
||||
),
|
||||
createMockPolicyView(
|
||||
number = 1,
|
||||
organizationId = "mockId-1",
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"allowedDomains":"earlier.example.com"}""",
|
||||
revisionDate = Instant.parse("2026-01-01T00:00:00Z"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"earlier.example.com",
|
||||
policyManager.getEffectiveSendPolicy().allowedDomains,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicy should return safe defaults when no org has any Send-related policy`() {
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
setUpSendPolicies()
|
||||
|
||||
assertEquals(
|
||||
EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
),
|
||||
policyManager.getEffectiveSendPolicy(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicyFlow should re-emit when the underlying policies change`() = runTest {
|
||||
val userStateJson = mockk<UserStateJson> {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val organizations = createMockOrganizationNetwork(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
)
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.SEND_CONTROLS,
|
||||
)
|
||||
} returns emptyList<PolicyView>().asSuccess()
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.SEND_OPTIONS,
|
||||
)
|
||||
} returns emptyList<PolicyView>().asSuccess()
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.DISABLE_SEND,
|
||||
)
|
||||
} returnsMany listOf(
|
||||
emptyList<PolicyView>().asSuccess(),
|
||||
listOf(createMockPolicyView(organizationId = organizations.id, enabled = true))
|
||||
.asSuccess(),
|
||||
)
|
||||
|
||||
mutableUserStateFlow.value = userStateJson
|
||||
mutableOrganizationsFlow.value = listOf(organizations)
|
||||
mutablePolicyFlow.value = listOf(
|
||||
createMockPolicy(organizationId = organizations.id, isEnabled = true),
|
||||
)
|
||||
|
||||
val expectedPolicy = EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
)
|
||||
|
||||
policyManager
|
||||
.getEffectiveSendPolicyFlow()
|
||||
.test {
|
||||
assertEquals(expectedPolicy, awaitItem())
|
||||
|
||||
mutablePolicyFlow.value = listOf(
|
||||
createMockPolicy(
|
||||
number = 2,
|
||||
organizationId = organizations.id,
|
||||
isEnabled = true,
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(expectedPolicy.copy(disableSend = true), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getEffectiveSendPolicyFlow should re-emit when the SendControls feature flag changes`() =
|
||||
runTest {
|
||||
val userStateJson = mockk<UserStateJson> {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val organizations = createMockOrganizationNetwork(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
)
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.SEND_CONTROLS,
|
||||
)
|
||||
} returns listOf(
|
||||
createMockPolicyView(
|
||||
organizationId = organizations.id,
|
||||
enabled = true,
|
||||
type = PolicyType.SEND_CONTROLS,
|
||||
data = """{"disableSend":false}""",
|
||||
),
|
||||
)
|
||||
.asSuccess()
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.DISABLE_SEND,
|
||||
)
|
||||
} returns listOf(
|
||||
createMockPolicyView(organizationId = organizations.id, enabled = true),
|
||||
)
|
||||
.asSuccess()
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.SEND_OPTIONS,
|
||||
)
|
||||
} returns emptyList<PolicyView>().asSuccess()
|
||||
|
||||
mutableUserStateFlow.value = userStateJson
|
||||
mutableOrganizationsFlow.value = listOf(organizations)
|
||||
mutablePolicyFlow.value = listOf(
|
||||
createMockPolicy(organizationId = organizations.id, isEnabled = true),
|
||||
)
|
||||
|
||||
val expectedPolicy = EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = true,
|
||||
whoCanAccess = null,
|
||||
)
|
||||
|
||||
policyManager
|
||||
.getEffectiveSendPolicyFlow()
|
||||
.test {
|
||||
// Flag off, so the organization's legacy DisableSend policy applies.
|
||||
assertEquals(expectedPolicy, awaitItem())
|
||||
|
||||
mutableSendControlsFlagFlow.value = true
|
||||
|
||||
// Flag on, so the organization's SendControls policy supersedes its legacy one.
|
||||
assertEquals(expectedPolicy.copy(disableSend = false), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the mocks required for [PolicyManagerImpl.getEffectiveSendPolicy] /
|
||||
* [PolicyManagerImpl.getEffectiveSendPolicyFlow] to return, per policy type, the given lists
|
||||
* of already-decoded [PolicyView]s.
|
||||
*/
|
||||
private fun setUpSendPolicies(
|
||||
disableSendPolicies: List<PolicyView> = emptyList(),
|
||||
sendControlsPolicies: List<PolicyView> = emptyList(),
|
||||
sendOptionsPolicies: List<PolicyView> = emptyList(),
|
||||
) {
|
||||
val userState: UserStateJson = mockk {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
every { authDiskSource.userState } returns userState
|
||||
every {
|
||||
authDiskSource.getOrganizations(USER_ID)
|
||||
} returns listOf(
|
||||
createMockOrganizationNetwork(number = 1, isEnabled = true, shouldUsePolicies = true),
|
||||
)
|
||||
every {
|
||||
authDiskSource.getPolicies(USER_ID)
|
||||
} returns listOf(createMockPolicy(organizationId = "mockId-1", isEnabled = true))
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.SEND_CONTROLS,
|
||||
)
|
||||
} returns sendControlsPolicies.asSuccess()
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.DISABLE_SEND,
|
||||
)
|
||||
} returns disableSendPolicies.asSuccess()
|
||||
every {
|
||||
authSdkSource.filterPolicies(
|
||||
policies = any(),
|
||||
organizations = any(),
|
||||
policyType = PolicyType.SEND_OPTIONS,
|
||||
)
|
||||
} returns sendOptionsPolicies.asSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
private const val USER_ID = "userId"
|
||||
|
||||
+17
-15
@@ -1,8 +1,6 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.policies.PolicyView
|
||||
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
@@ -10,8 +8,8 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
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.model.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockPolicyView
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
@@ -36,14 +34,10 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
||||
private val firstTimeActionManager: FirstTimeActionManager = mockk {
|
||||
every { allSettingsBadgeCountFlow } returns mutableSettingsBadgeCountFlow
|
||||
}
|
||||
private val mutableDisableSendsPolicyFlow = MutableStateFlow<List<PolicyView>>(emptyList())
|
||||
private val mutableEffectiveSendPolicyFlow = MutableStateFlow(DEFAULT_EFFECTIVE_SEND_POLICY)
|
||||
private val policyManager: PolicyManager = mockk {
|
||||
every {
|
||||
getActivePoliciesFlow(PolicyType.DISABLE_SEND)
|
||||
} returns mutableDisableSendsPolicyFlow
|
||||
every {
|
||||
getActivePolicies(PolicyType.DISABLE_SEND)
|
||||
} answers { mutableDisableSendsPolicyFlow.value }
|
||||
every { getEffectiveSendPolicyFlow() } returns mutableEffectiveSendPolicyFlow
|
||||
every { getEffectiveSendPolicy() } answers { mutableEffectiveSendPolicyFlow.value }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@@ -326,20 +320,19 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `DISABLE_SEND policy flow update with disabled policy should set areSendsDisabled to false`() =
|
||||
fun `effective send policy flow update should set areSendsDisabled to match disableSend`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(DEFAULT_STATE.copy(areSendsDisabled = false), awaitItem())
|
||||
|
||||
mutableDisableSendsPolicyFlow.emit(
|
||||
listOf(createMockPolicyView(type = PolicyType.DISABLE_SEND)),
|
||||
mutableEffectiveSendPolicyFlow.emit(
|
||||
DEFAULT_EFFECTIVE_SEND_POLICY.copy(disableSend = true),
|
||||
)
|
||||
assertEquals(DEFAULT_STATE.copy(areSendsDisabled = true), awaitItem())
|
||||
|
||||
mutableDisableSendsPolicyFlow.emit(emptyList())
|
||||
mutableEffectiveSendPolicyFlow.emit(DEFAULT_EFFECTIVE_SEND_POLICY)
|
||||
assertEquals(DEFAULT_STATE.copy(areSendsDisabled = false), awaitItem())
|
||||
}
|
||||
}
|
||||
@@ -362,3 +355,12 @@ private val DEFAULT_STATE = VaultUnlockedNavBarState(
|
||||
notificationState = DEFAULT_NOTIFICATION_STATE,
|
||||
areSendsDisabled = false,
|
||||
)
|
||||
|
||||
private val DEFAULT_EFFECTIVE_SEND_POLICY = EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.data.repository.model.Environment
|
||||
import com.bitwarden.data.repository.util.baseWebSendUrl
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
|
||||
import com.bitwarden.ui.platform.manager.snackbar.SnackbarRelayManager
|
||||
@@ -19,6 +18,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
@@ -73,8 +73,8 @@ class SendViewModelTest : BaseViewModelTest() {
|
||||
every { sendDataStateFlow } returns mutableSendDataFlow
|
||||
}
|
||||
private val policyManager: PolicyManager = mockk {
|
||||
every { getActivePolicies(type = PolicyType.DISABLE_SEND) } returns emptyList()
|
||||
every { getActivePoliciesFlow(type = PolicyType.DISABLE_SEND) } returns emptyFlow()
|
||||
every { getEffectiveSendPolicy() } returns DEFAULT_EFFECTIVE_SEND_POLICY
|
||||
every { getEffectiveSendPolicyFlow() } returns emptyFlow()
|
||||
}
|
||||
|
||||
private val networkConnectionManager: NetworkConnectionManager = mockk {
|
||||
@@ -823,6 +823,15 @@ private val DEFAULT_STATE: SendState = SendState(
|
||||
isPremiumUser = false,
|
||||
)
|
||||
|
||||
private val DEFAULT_EFFECTIVE_SEND_POLICY = EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
)
|
||||
|
||||
private val DEFAULT_USER_ACCOUNT_STATE = UserState.Account(
|
||||
userId = "user_id_1",
|
||||
name = "Bit",
|
||||
|
||||
+4
@@ -1578,6 +1578,10 @@ private val DEFAULT_STATE = AddEditSendState(
|
||||
isShared = false,
|
||||
baseWebSendUrl = "https://vault.bitwarden.com/#/send/",
|
||||
policyDisablesSend = false,
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
whoCanAccess = null,
|
||||
sendType = SendItemType.TEXT,
|
||||
isPremium = true,
|
||||
)
|
||||
|
||||
+18
-19
@@ -6,7 +6,6 @@ import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
|
||||
import com.bitwarden.data.repository.model.Environment
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.send.SendView
|
||||
import com.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
|
||||
@@ -17,18 +16,17 @@ import com.bitwarden.ui.util.Text
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.billing.manager.PremiumStateManager
|
||||
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.model.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConnectionManager
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
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.sdk.model.createMockPolicyView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
@@ -56,7 +54,6 @@ import io.mockk.verify
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
@@ -96,8 +93,7 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
|
||||
every { getSendStateFlow(any()) } returns mutableSendDataStateFlow
|
||||
}
|
||||
private val policyManager: PolicyManager = mockk {
|
||||
every { getActivePolicies(type = PolicyType.DISABLE_SEND) } returns emptyList()
|
||||
every { getActivePolicies(type = PolicyType.SEND_OPTIONS) } returns emptyList()
|
||||
every { getEffectiveSendPolicy() } returns DEFAULT_EFFECTIVE_SEND_POLICY
|
||||
}
|
||||
private val networkConnectionManager = mockk<NetworkConnectionManager> {
|
||||
every { isNetworkConnected } returns true
|
||||
@@ -139,20 +135,10 @@ class AddEditSendViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when a sendOption includes shouldDisableHideEmail`() {
|
||||
fun `initial state should be correct when the effective policy disables hide email`() {
|
||||
every {
|
||||
policyManager.getActivePolicies(type = PolicyType.SEND_OPTIONS)
|
||||
} returns listOf(
|
||||
createMockPolicyView(
|
||||
id = "123",
|
||||
type = PolicyType.SEND_OPTIONS,
|
||||
enabled = true,
|
||||
data = Json.encodeToString(
|
||||
PolicyInformation.SendOptions(shouldDisableHideEmail = true),
|
||||
),
|
||||
organizationId = "id2",
|
||||
),
|
||||
)
|
||||
policyManager.getEffectiveSendPolicy()
|
||||
} returns DEFAULT_EFFECTIVE_SEND_POLICY.copy(disableHideEmail = true)
|
||||
val viewModel = createViewModel()
|
||||
val viewState = DEFAULT_VIEW_STATE.copy(
|
||||
common = DEFAULT_COMMON_STATE.copy(
|
||||
@@ -1520,10 +1506,23 @@ private val DEFAULT_STATE = AddEditSendState(
|
||||
isShared = false,
|
||||
baseWebSendUrl = DEFAULT_ENVIRONMENT_URL,
|
||||
policyDisablesSend = false,
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
whoCanAccess = null,
|
||||
sendType = SendItemType.TEXT,
|
||||
isPremium = true,
|
||||
)
|
||||
|
||||
private val DEFAULT_EFFECTIVE_SEND_POLICY = EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
)
|
||||
|
||||
private val DEFAULT_ACCOUNT = UserState.Account(
|
||||
userId = "activeUserId",
|
||||
name = "Active User",
|
||||
|
||||
+12
-2
@@ -78,6 +78,7 @@ import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
|
||||
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.EffectiveSendPolicy
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.OrganizationEvent
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
@@ -221,8 +222,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||
private val mutableActivePoliciesFlow: MutableStateFlow<List<PolicyView>> =
|
||||
MutableStateFlow(emptyList())
|
||||
private val policyManager: PolicyManager = mockk {
|
||||
every { getActivePolicies(type = PolicyType.DISABLE_SEND) } returns emptyList()
|
||||
every { getActivePoliciesFlow(type = PolicyType.DISABLE_SEND) } returns emptyFlow()
|
||||
every { getEffectiveSendPolicy() } returns DEFAULT_EFFECTIVE_SEND_POLICY
|
||||
every { getEffectiveSendPolicyFlow() } returns emptyFlow()
|
||||
every {
|
||||
getActivePoliciesFlow(type = PolicyType.RESTRICTED_ITEM_TYPES)
|
||||
} returns mutableActivePoliciesFlow
|
||||
@@ -6779,4 +6780,13 @@ private val DEFAULT_USER_STATE = UserState(
|
||||
accounts = listOf(DEFAULT_ACCOUNT),
|
||||
)
|
||||
|
||||
private val DEFAULT_EFFECTIVE_SEND_POLICY = EffectiveSendPolicy(
|
||||
allowedDomains = null,
|
||||
allowedSendTypes = null,
|
||||
deletionHours = null,
|
||||
disableHideEmail = false,
|
||||
disableSend = false,
|
||||
whoCanAccess = null,
|
||||
)
|
||||
|
||||
private const val DEFAULT_RELYING_PARTY_ID = "www.bitwarden.com"
|
||||
|
||||
Reference in New Issue
Block a user