diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerImpl.kt index 7b21fbe258..53e4550683 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerImpl.kt @@ -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?, - organizations: List?, - isPoliciesInAcceptedStateEnabled: Boolean, + organizations: List?, ): List? = 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, -) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerTest.kt index d78c2a7ee0..1db5c73b0c 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/PolicyManagerTest.kt @@ -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 { - 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 { - 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(), 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 { - 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(), 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 { - 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().asSuccess() - every { - authSdkSource.filterPolicies( - policies = any(), - organizations = any(), - policyType = PolicyType.SEND_OPTIONS, - ) - } returns emptyList().asSuccess() - every { - authSdkSource.filterPolicies( - policies = any(), - organizations = any(), - policyType = PolicyType.DISABLE_SEND, - ) - } returnsMany listOf( - emptyList().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 { + 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().asSuccess() + every { + authSdkSource.filterPolicies( + policies = any(), + organizations = any(), + policyType = PolicyType.SEND_OPTIONS, + ) + } returns emptyList().asSuccess() + every { + authSdkSource.filterPolicies( + policies = any(), + organizations = any(), + policyType = PolicyType.DISABLE_SEND, + ) + } returnsMany listOf( + emptyList().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 { diff --git a/core/src/main/kotlin/com/bitwarden/core/data/manager/model/FlagKey.kt b/core/src/main/kotlin/com/bitwarden/core/data/manager/model/FlagKey.kt index 86abc7657e..9ce83cd50f 100644 --- a/core/src/main/kotlin/com/bitwarden/core/data/manager/model/FlagKey.kt +++ b/core/src/main/kotlin/com/bitwarden/core/data/manager/model/FlagKey.kt @@ -44,7 +44,6 @@ sealed class FlagKey { NewItemTypes, DebugDisableSelfHostPremiumCheck, FillAssistTargetingRules, - PoliciesInAcceptedState, FedRamp, Vfo1Foundation, SendControls, @@ -169,14 +168,6 @@ sealed class FlagKey { override val defaultValue: Boolean = false } - /** - * Data object holding the feature flag key for the Policies In Accepted State feature. - */ - data object PoliciesInAcceptedState : FlagKey() { - 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. */ diff --git a/core/src/test/kotlin/com/bitwarden/core/data/manager/model/FlagKeyTest.kt b/core/src/test/kotlin/com/bitwarden/core/data/manager/model/FlagKeyTest.kt index b9d516b34a..3d22a41bd3 100644 --- a/core/src/test/kotlin/com/bitwarden/core/data/manager/model/FlagKeyTest.kt +++ b/core/src/test/kotlin/com/bitwarden/core/data/manager/model/FlagKeyTest.kt @@ -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, diff --git a/ui/src/main/kotlin/com/bitwarden/ui/platform/components/debug/FeatureFlagListItems.kt b/ui/src/main/kotlin/com/bitwarden/ui/platform/components/debug/FeatureFlagListItems.kt index 1145d16d0f..55daf176fe 100644 --- a/ui/src/main/kotlin/com/bitwarden/ui/platform/components/debug/FeatureFlagListItems.kt +++ b/ui/src/main/kotlin/com/bitwarden/ui/platform/components/debug/FeatureFlagListItems.kt @@ -38,7 +38,6 @@ fun FlagKey.ListItemContent( FlagKey.NewItemTypes, FlagKey.FillAssistTargetingRules, FlagKey.DebugDisableSelfHostPremiumCheck, - FlagKey.PoliciesInAcceptedState, FlagKey.FedRamp, FlagKey.Vfo1Foundation, FlagKey.SendControls, @@ -104,7 +103,6 @@ private fun FlagKey.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) } diff --git a/ui/src/main/res/values/strings_non_localized.xml b/ui/src/main/res/values/strings_non_localized.xml index 3e2531fd71..ae048c2fa5 100644 --- a/ui/src/main/res/values/strings_non_localized.xml +++ b/ui/src/main/res/values/strings_non_localized.xml @@ -58,7 +58,6 @@ New Item Types Fill Assist Targeting Rules Debug: Disable self-host premium check - Policies in accepted state FedRAMP VFO-1 foundation Send Controls