diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt index a2a53dd429..e83908000c 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt @@ -1,5 +1,7 @@ package com.x8bit.bitwarden.data.auth.repository.model +import com.bitwarden.network.model.SendTypeJson +import com.bitwarden.network.model.SendAccessTypeJson import com.bitwarden.network.model.SyncResponseJson import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -113,6 +115,42 @@ sealed class PolicyInformation { val shouldDisableHideEmail: Boolean?, ) : PolicyInformation() + /** + * Represents a policy enforcing rules on the creation and sharing of Sends. Supersedes the + * disable-send policy and [SendOptions] when the `pm-31885-send-controls` feature flag is + * active. + * + * @property disableSend Whether the ability to create and edit Sends is disabled. + * @property disableHideEmail Whether the user should have the ability to hide their email + * address from Send recipients. + * @property whoCanAccess The access type Sends are restricted to, if any. + * @property allowedDomains A comma-separated list of email domains recipients must belong to + * when [whoCanAccess] is [SendAccessTypeJson.SPECIFIC_PEOPLE]. + * @property deletionHours The number of hours until a Send is deleted, if enforced. + * @property allowedSendTypes The types of Sends that are allowed to be created, if + * restricted. + */ + @Serializable + data class SendControls( + @SerialName("disableSend") + val disableSend: Boolean?, + + @SerialName("disableHideEmail") + val disableHideEmail: Boolean?, + + @SerialName("whoCanAccess") + val whoCanAccess: SendAccessTypeJson?, + + @SerialName("allowedDomains") + val allowedDomains: String?, + + @SerialName("deletionHours") + val deletionHours: Int?, + + @SerialName("allowedSendTypes") + val allowedSendTypes: List?, + ) : PolicyInformation() + /** * Represents a policy enforcing rules on the user's vault timeout settings. */ diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt index abb196b25e..4779fd8142 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt @@ -153,6 +153,10 @@ val PolicyView.policyInformation: PolicyInformation? JSON.decodeFromStringOrNull(it) } + PolicyType.SEND_CONTROLS -> { + JSON.decodeFromStringOrNull(it) + } + else -> null } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensions.kt index 9dd815a80e..c07cc84653 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensions.kt @@ -34,6 +34,7 @@ inline fun getPolicyType(): PolicyType = PolicyInformation.MasterPassword::class.java -> PolicyType.MASTER_PASSWORD PolicyInformation.PasswordGenerator::class.java -> PolicyType.PASSWORD_GENERATOR PolicyInformation.SendOptions::class.java -> PolicyType.SEND_OPTIONS + PolicyInformation.SendControls::class.java -> PolicyType.SEND_CONTROLS PolicyInformation.VaultTimeout::class.java -> PolicyType.MAXIMUM_VAULT_TIMEOUT else -> { diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt index 0f8b12bfba..5b105e2027 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt @@ -1,6 +1,8 @@ package com.x8bit.bitwarden.data.auth.repository.util import com.bitwarden.network.model.OrganizationType +import com.bitwarden.network.model.SendTypeJson +import com.bitwarden.network.model.SendAccessTypeJson import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.createMockOrganizationNetwork import com.bitwarden.network.model.createMockPermissions @@ -172,6 +174,27 @@ class SyncResponseJsonExtensionsTest { ) } + @Test + fun `policyInformation converts the SendControls Json data to policy information`() { + val policyInformation = PolicyInformation.SendControls( + disableSend = false, + disableHideEmail = true, + whoCanAccess = SendAccessTypeJson.SPECIFIC_PEOPLE, + allowedDomains = "bitwarden.com", + deletionHours = 168, + allowedSendTypes = listOf(SendTypeJson.TEXT), + ) + val policy = createMockPolicyView( + type = PolicyType.SEND_CONTROLS, + data = Json.encodeToString(policyInformation), + ) + + assertEquals( + policyInformation, + policy.policyInformation, + ) + } + @Test fun `policyInformation returns null policy information for null data`() { val masterPasswordPolicy = createMockPolicyView( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensionsTest.kt index 759f73de6f..a4c5cfc815 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/util/PolicyManagerExtensionsTest.kt @@ -70,6 +70,14 @@ class PolicyManagerExtensionsTest { ) } + @Test + fun `getPolicyType with SendControls should map to appropriate PolicyTypeJson`() { + assertEquals( + PolicyType.SEND_CONTROLS, + getPolicyType(), + ) + } + @Test fun `getPolicyType with VaultTimeout should map to appropriate PolicyTypeJson`() { assertEquals( 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 726813c1fe..6864c0bc38 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 @@ -46,6 +46,8 @@ sealed class FlagKey { FillAssistTargetingRules, PoliciesInAcceptedState, FedRamp, + SendControls, + SendControlsExistingSends, ) } } @@ -189,6 +191,23 @@ sealed class FlagKey { override val defaultValue: Boolean = false } + /** + * Data object holding the feature flag key for the consolidated Send Controls policy. + */ + data object SendControls : FlagKey() { + override val keyName: String = "pm-31885-send-controls" + override val defaultValue: Boolean = false + } + + /** + * Data object holding the feature flag key for enforcing the Send Controls policy against + * Sends created before the policy existed. + */ + data object SendControlsExistingSends : FlagKey() { + override val keyName: String = "pm-31885-send-controls-existing-sends" + override val defaultValue: Boolean = false + } + //region Dummy keys for testing /** * Data object holding the key for a [Boolean] flag to be used in tests. 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 463c114454..05c7db9e8a 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 @@ -64,6 +64,14 @@ class FlagKeyTest { FlagKey.FedRamp.keyName, "fedramp-gov-region", ) + assertEquals( + FlagKey.SendControls.keyName, + "pm-31885-send-controls", + ) + assertEquals( + FlagKey.SendControlsExistingSends.keyName, + "pm-31885-send-controls-existing-sends", + ) } @Test @@ -84,6 +92,8 @@ class FlagKeyTest { FlagKey.ManageDevices, FlagKey.PoliciesInAcceptedState, FlagKey.FedRamp, + FlagKey.SendControls, + FlagKey.SendControlsExistingSends, ).all { !it.defaultValue }, diff --git a/network/src/main/kotlin/com/bitwarden/network/model/SendAccessTypeJson.kt b/network/src/main/kotlin/com/bitwarden/network/model/SendAccessTypeJson.kt new file mode 100644 index 0000000000..fee64b9273 --- /dev/null +++ b/network/src/main/kotlin/com/bitwarden/network/model/SendAccessTypeJson.kt @@ -0,0 +1,37 @@ +package com.bitwarden.network.model + +import androidx.annotation.Keep +import com.bitwarden.core.data.serializer.BaseEnumeratedIntSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Represents who is allowed to view a Send under the `SendControls` policy. + */ +@Serializable(SendAccessTypeSerializer::class) +enum class SendAccessTypeJson { + /** + * Anyone with the link can view the Send. + */ + @SerialName("0") + ANY, + + /** + * Only individuals with the password set on the Send can view it. + */ + @SerialName("1") + PASSWORD_PROTECTED, + + /** + * Only specific people, identified by email, can view the Send. + */ + @SerialName("2") + SPECIFIC_PEOPLE, +} + +@Keep +private class SendAccessTypeSerializer : + BaseEnumeratedIntSerializer( + className = "SendAccessTypeJson", + values = SendAccessTypeJson.entries.toTypedArray(), + ) 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 76968a5ccf..1d2d1d1233 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 @@ -40,6 +40,8 @@ fun FlagKey.ListItemContent( FlagKey.DebugDisableSelfHostPremiumCheck, FlagKey.PoliciesInAcceptedState, FlagKey.FedRamp, + FlagKey.SendControls, + FlagKey.SendControlsExistingSends, -> { @Suppress("UNCHECKED_CAST") BooleanFlagItem( @@ -104,4 +106,8 @@ private fun FlagKey.getDisplayLabel(): String = when (this) { } FlagKey.FedRamp -> stringResource(BitwardenString.fed_ramp) + FlagKey.SendControls -> stringResource(BitwardenString.send_controls) + FlagKey.SendControlsExistingSends -> { + stringResource(BitwardenString.send_controls_existing_sends) + } } diff --git a/ui/src/main/res/values/strings_non_localized.xml b/ui/src/main/res/values/strings_non_localized.xml index 1143bf45b6..40cf0ba0f5 100644 --- a/ui/src/main/res/values/strings_non_localized.xml +++ b/ui/src/main/res/values/strings_non_localized.xml @@ -57,6 +57,8 @@ Debug: Disable self-host premium check Policies in accepted state FedRAMP + Send Controls + Send Controls - Existing Sends