mirror of
https://github.com/bitwarden/android.git
synced 2026-10-21 10:40:49 -05:00
PM-39863: feat: Remove the Accepted Policies feature flag (#7416)
This commit is contained in:
1 parent
a608ec6cd3
commit
bb93209ef9
6 files changed
+72
-285
No files matched your search
@@ -1,8 +1,6 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.organizations.OrganizationUserStatusType
|
||||
import com.bitwarden.organizations.OrganizationUserType
|
||||
import com.bitwarden.policies.OrganizationUserPolicyContext
|
||||
import com.bitwarden.policies.PolicyType
|
||||
import com.bitwarden.policies.PolicyView
|
||||
@@ -86,14 +84,7 @@ class PolicyManagerImpl(
|
||||
?.toSdkPolicyViews(),
|
||||
organizations = authDiskSource
|
||||
.getOrganizations(userId = userId)
|
||||
?.map {
|
||||
OrganizationPolicyData(
|
||||
organizationUserPolicyContext = it.toSdkOrganizationPolicyContext(),
|
||||
organizationShouldUsePolicies = it.permissions.shouldManagePolicies,
|
||||
)
|
||||
},
|
||||
isPoliciesInAcceptedStateEnabled = featureFlagManager
|
||||
.getFeatureFlag(key = FlagKey.PoliciesInAcceptedState),
|
||||
?.map { it.toSdkOrganizationPolicyContext() },
|
||||
)
|
||||
.orEmpty()
|
||||
|
||||
@@ -113,21 +104,12 @@ class PolicyManagerImpl(
|
||||
.map { it?.toSdkPolicyViews() },
|
||||
authDiskSource
|
||||
.getOrganizationsFlow(userId = userId)
|
||||
.map { organizations ->
|
||||
organizations?.map {
|
||||
OrganizationPolicyData(
|
||||
organizationUserPolicyContext = it.toSdkOrganizationPolicyContext(),
|
||||
organizationShouldUsePolicies = it.permissions.shouldManagePolicies,
|
||||
)
|
||||
}
|
||||
},
|
||||
featureFlagManager.getFeatureFlagFlow(key = FlagKey.PoliciesInAcceptedState),
|
||||
) { policies, organizations, isEnabled ->
|
||||
.map { organizations -> organizations?.map { it.toSdkOrganizationPolicyContext() } },
|
||||
) { policies, organizations ->
|
||||
filterPolicies(
|
||||
type = type,
|
||||
policies = policies,
|
||||
organizations = organizations,
|
||||
isPoliciesInAcceptedStateEnabled = isEnabled,
|
||||
)
|
||||
}
|
||||
// We do not have any policies yet if it is null, so do not emit at all.
|
||||
@@ -136,66 +118,20 @@ class PolicyManagerImpl(
|
||||
private fun filterPolicies(
|
||||
type: PolicyType,
|
||||
policies: List<PolicyView>?,
|
||||
organizations: List<OrganizationPolicyData>?,
|
||||
isPoliciesInAcceptedStateEnabled: Boolean,
|
||||
organizations: List<OrganizationUserPolicyContext>?,
|
||||
): List<PolicyView>? =
|
||||
when {
|
||||
policies == null -> null
|
||||
policies.isEmpty() -> emptyList()
|
||||
isPoliciesInAcceptedStateEnabled -> {
|
||||
else -> {
|
||||
authSdkSource
|
||||
.filterPolicies(
|
||||
policies = policies,
|
||||
policyType = type,
|
||||
organizations = organizations
|
||||
?.map { it.organizationUserPolicyContext }
|
||||
.orEmpty(),
|
||||
organizations = organizations.orEmpty(),
|
||||
)
|
||||
.getOrElse { emptyList() }
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Legacy flow
|
||||
val organizationIdsWithActivePolicies = organizations
|
||||
?.filter {
|
||||
@Suppress("MaxLineLength")
|
||||
it.organizationUserPolicyContext.usePolicies &&
|
||||
(it.organizationUserPolicyContext.status == OrganizationUserStatusType.ACCEPTED ||
|
||||
it.organizationUserPolicyContext.status == OrganizationUserStatusType.CONFIRMED) &&
|
||||
!it.isOrganizationExemptFromPolicies(policyType = type)
|
||||
}
|
||||
?.map { it.organizationUserPolicyContext.id }
|
||||
.orEmpty()
|
||||
return policies.filter {
|
||||
it.type == type &&
|
||||
it.enabled &&
|
||||
organizationIdsWithActivePolicies.contains(it.organizationId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to determine if the organization is exempt from policies.
|
||||
*/
|
||||
private fun OrganizationPolicyData.isOrganizationExemptFromPolicies(
|
||||
policyType: PolicyType,
|
||||
): Boolean =
|
||||
when (policyType) {
|
||||
PolicyType.MAXIMUM_VAULT_TIMEOUT -> {
|
||||
this.organizationUserPolicyContext.role == OrganizationUserType.OWNER
|
||||
}
|
||||
|
||||
PolicyType.MASTER_PASSWORD,
|
||||
PolicyType.PASSWORD_GENERATOR,
|
||||
PolicyType.REMOVE_UNLOCK_WITH_PIN,
|
||||
PolicyType.RESTRICTED_ITEM_TYPES,
|
||||
-> false
|
||||
|
||||
else -> {
|
||||
this.organizationUserPolicyContext.role == OrganizationUserType.OWNER ||
|
||||
this.organizationUserPolicyContext.role == OrganizationUserType.ADMIN ||
|
||||
this.organizationShouldUsePolicies
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,8 +190,3 @@ class PolicyManagerImpl(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class OrganizationPolicyData(
|
||||
val organizationUserPolicyContext: OrganizationUserPolicyContext,
|
||||
val organizationShouldUsePolicies: Boolean,
|
||||
)
|
||||
+66
-193
@@ -3,7 +3,6 @@ package com.x8bit.bitwarden.data.platform.manager
|
||||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.data.manager.model.FlagKey
|
||||
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
|
||||
@@ -39,15 +38,8 @@ class PolicyManagerTest {
|
||||
every { getOrganizationsFlow(USER_ID) } returns mutableOrganizationsFlow
|
||||
}
|
||||
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)
|
||||
} returns mutablePoliciesInAcceptedStateFlagFlow
|
||||
every { getFeatureFlag(key = FlagKey.PoliciesInAcceptedState) } answers {
|
||||
mutablePoliciesInAcceptedStateFlagFlow.value
|
||||
}
|
||||
every {
|
||||
getFeatureFlagFlow(key = FlagKey.SendControls)
|
||||
} returns mutableSendControlsFlagFlow
|
||||
@@ -123,124 +115,6 @@ class PolicyManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getActivePoliciesFlow when feature flag is false should emit policies using legacy filtering`() =
|
||||
runTest {
|
||||
mutablePoliciesInAcceptedStateFlagFlow.value = false
|
||||
|
||||
val userStateJson = mockk<UserStateJson> {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val organizations = createMockOrganizationNetwork(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
)
|
||||
val storedPolicyOne = createMockPolicy(
|
||||
isEnabled = true,
|
||||
number = 1,
|
||||
organizationId = organizations.id,
|
||||
type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT,
|
||||
)
|
||||
val expectedPolicyOne = createMockPolicyView(
|
||||
enabled = true,
|
||||
number = 1,
|
||||
organizationId = organizations.id,
|
||||
type = PolicyType.MAXIMUM_VAULT_TIMEOUT,
|
||||
)
|
||||
val storedPolicyTwo = createMockPolicy(
|
||||
isEnabled = true,
|
||||
number = 2,
|
||||
organizationId = organizations.id,
|
||||
type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT,
|
||||
)
|
||||
val expectedPolicyTwo = createMockPolicyView(
|
||||
enabled = true,
|
||||
number = 2,
|
||||
organizationId = organizations.id,
|
||||
type = PolicyType.MAXIMUM_VAULT_TIMEOUT,
|
||||
)
|
||||
|
||||
mutableUserStateFlow.value = userStateJson
|
||||
mutableOrganizationsFlow.value = listOf(organizations)
|
||||
mutablePolicyFlow.value = listOf(storedPolicyOne)
|
||||
|
||||
policyManager
|
||||
.getActivePoliciesFlow(type = PolicyType.MAXIMUM_VAULT_TIMEOUT)
|
||||
.test {
|
||||
assertEquals(listOf(expectedPolicyOne), awaitItem())
|
||||
|
||||
mutablePolicyFlow.value = listOf(storedPolicyOne, storedPolicyTwo)
|
||||
assertEquals(listOf(expectedPolicyOne, expectedPolicyTwo), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getActivePoliciesFlow when feature flag is false should emit empty list when organization status is below ACCEPTED`() =
|
||||
runTest {
|
||||
mutablePoliciesInAcceptedStateFlagFlow.value = false
|
||||
|
||||
val userStateJson = mockk<UserStateJson> {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val organizations = createMockOrganizationNetwork(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = true,
|
||||
status = OrganizationStatusType.INVITED,
|
||||
)
|
||||
val storedPolicy = createMockPolicy(
|
||||
isEnabled = true,
|
||||
number = 1,
|
||||
organizationId = organizations.id,
|
||||
type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT,
|
||||
)
|
||||
|
||||
mutableUserStateFlow.value = userStateJson
|
||||
mutableOrganizationsFlow.value = listOf(organizations)
|
||||
mutablePolicyFlow.value = listOf(storedPolicy)
|
||||
|
||||
policyManager
|
||||
.getActivePoliciesFlow(type = PolicyType.MAXIMUM_VAULT_TIMEOUT)
|
||||
.test {
|
||||
assertEquals(emptyList<PolicyView>(), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getActivePoliciesFlow when feature flag is false should emit empty list when organization does not use policies`() =
|
||||
runTest {
|
||||
mutablePoliciesInAcceptedStateFlagFlow.value = false
|
||||
|
||||
val userStateJson = mockk<UserStateJson> {
|
||||
every { activeUserId } returns USER_ID
|
||||
}
|
||||
val organizations = createMockOrganizationNetwork(
|
||||
number = 1,
|
||||
isEnabled = true,
|
||||
shouldUsePolicies = false,
|
||||
)
|
||||
val storedPolicy = createMockPolicy(
|
||||
isEnabled = true,
|
||||
number = 1,
|
||||
organizationId = organizations.id,
|
||||
type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT,
|
||||
)
|
||||
|
||||
mutableUserStateFlow.value = userStateJson
|
||||
mutableOrganizationsFlow.value = listOf(organizations)
|
||||
mutablePolicyFlow.value = listOf(storedPolicy)
|
||||
|
||||
policyManager
|
||||
.getActivePoliciesFlow(type = PolicyType.MAXIMUM_VAULT_TIMEOUT)
|
||||
.test {
|
||||
assertEquals(emptyList<PolicyView>(), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getActivePolicies returns empty list if user id is null`() {
|
||||
every { authDiskSource.userState } returns null
|
||||
@@ -995,76 +869,75 @@ class PolicyManagerTest {
|
||||
)
|
||||
}
|
||||
|
||||
@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())
|
||||
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 {
|
||||
|
||||
@@ -44,7 +44,6 @@ sealed class FlagKey<out T : Any> {
|
||||
NewItemTypes,
|
||||
DebugDisableSelfHostPremiumCheck,
|
||||
FillAssistTargetingRules,
|
||||
PoliciesInAcceptedState,
|
||||
FedRamp,
|
||||
Vfo1Foundation,
|
||||
SendControls,
|
||||
@@ -169,14 +168,6 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for the Policies In Accepted State feature.
|
||||
*/
|
||||
data object PoliciesInAcceptedState : FlagKey<Boolean>() {
|
||||
override val keyName: String get() = "pm-34145-policies-in-accepted-state"
|
||||
override val defaultValue: Boolean get() = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for the FedRAMP feature.
|
||||
*/
|
||||
|
||||
@@ -56,10 +56,6 @@ class FlagKeyTest {
|
||||
FlagKey.ManageDevices.keyName,
|
||||
"pm-4516-devices-add-last-activity-date",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.PoliciesInAcceptedState.keyName,
|
||||
"pm-34145-policies-in-accepted-state",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.FedRamp.keyName,
|
||||
"fedramp-gov-region",
|
||||
@@ -106,7 +102,6 @@ class FlagKeyTest {
|
||||
FlagKey.NewItemTypes,
|
||||
FlagKey.FillAssistTargetingRules,
|
||||
FlagKey.ManageDevices,
|
||||
FlagKey.PoliciesInAcceptedState,
|
||||
FlagKey.FedRamp,
|
||||
FlagKey.Vfo1Foundation,
|
||||
FlagKey.SendControls,
|
||||
|
||||
@@ -38,7 +38,6 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.NewItemTypes,
|
||||
FlagKey.FillAssistTargetingRules,
|
||||
FlagKey.DebugDisableSelfHostPremiumCheck,
|
||||
FlagKey.PoliciesInAcceptedState,
|
||||
FlagKey.FedRamp,
|
||||
FlagKey.Vfo1Foundation,
|
||||
FlagKey.SendControls,
|
||||
@@ -104,7 +103,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
FlagKey.V2EncryptionTde -> stringResource(BitwardenString.v2_encryption_tde)
|
||||
FlagKey.NewItemTypes -> stringResource(BitwardenString.new_item_types)
|
||||
FlagKey.FillAssistTargetingRules -> stringResource(BitwardenString.fill_assist_targeting_rules)
|
||||
FlagKey.PoliciesInAcceptedState -> stringResource(BitwardenString.policies_in_accepted_state)
|
||||
FlagKey.DebugDisableSelfHostPremiumCheck -> {
|
||||
stringResource(BitwardenString.debug_disable_self_host_premium_check)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
<string name="new_item_types">New Item Types</string>
|
||||
<string name="fill_assist_targeting_rules">Fill Assist Targeting Rules</string>
|
||||
<string name="debug_disable_self_host_premium_check">Debug: Disable self-host premium check</string>
|
||||
<string name="policies_in_accepted_state">Policies in accepted state</string>
|
||||
<string name="fed_ramp">FedRAMP</string>
|
||||
<string name="vfo1_foundation">VFO-1 foundation</string>
|
||||
<string name="send_controls">Send Controls</string>
|
||||
|
||||
Reference in new issue
Block a user