diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt new file mode 100644 index 000000000..163fdc7ea --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/PolicyInformation.kt @@ -0,0 +1,45 @@ +package com.x8bit.bitwarden.data.auth.repository.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * The policy information decoded from the [data] parameter of the [SyncResponseJson.Policy] object. + */ +@Serializable +sealed class PolicyInformation { + /** + * Represents a policy enforcing rules on the user's master password. + * + * @property minLength The minimum length of the password. + * @property minComplexity The minimum complexity of the password. + * @property requireUpper Whether the password requires upper case letters. + * @property requireLower Whether the password requires lower case letters. + * @property requireNumbers Whether the password requires numbers. + * @property requireSpecial Whether the password requires special characters. + * @property enforceOnLogin Whether the password should be enforced on login. + */ + @Serializable + data class MasterPassword( + @SerialName("minLength") + val minLength: Int?, + + @SerialName("minComplexity") + val minComplexity: Int?, + + @SerialName("requireUpper") + val requireUpper: Boolean?, + + @SerialName("requireLower") + val requireLower: Boolean?, + + @SerialName("requireNumbers") + val requireNumbers: Boolean?, + + @SerialName("requireSpecial") + val requireSpecial: Boolean?, + + @SerialName("enforceOnLogin") + val enforceOnLogin: Boolean?, + ) : PolicyInformation() +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt index 019009580..372351d97 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensions.kt @@ -1,7 +1,10 @@ package com.x8bit.bitwarden.data.auth.repository.util import com.x8bit.bitwarden.data.auth.repository.model.Organization +import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson +import kotlinx.serialization.json.Json /** * Maps the given [SyncResponseJson.Profile.Organization] to an [Organization]. @@ -18,3 +21,17 @@ fun SyncResponseJson.Profile.Organization.toOrganization(): Organization = */ fun List.toOrganizations(): List = this.map { it.toOrganization() } + +/** + * Convert the JSON data of the [SyncResponseJson.Policy] object into [PolicyInformation] data. + */ +val SyncResponseJson.Policy.policyInformation: PolicyInformation? + get() = data?.toString()?.let { + when (type) { + PolicyTypeJson.MASTER_PASSWORD -> { + Json.decodeFromString(it) + } + + else -> null + } + } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt index 33162e861..b9cc26407 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponseJson.kt @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.vault.datasource.network.model import kotlinx.serialization.Contextual import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonObject import java.time.ZonedDateTime /** @@ -101,6 +102,7 @@ data class SyncResponseJson( * @property id The ID of the policy. * @property type The type of policy. * @property isEnabled If the policy is enabled or not. + * @property data Any extra data about the policy, in the form of a JSON string. */ @Serializable data class Policy( @@ -115,6 +117,9 @@ data class SyncResponseJson( @SerialName("enabled") val isEnabled: Boolean, + + @SerialName("data") + val data: JsonObject?, ) /** diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt index d3a53d624..725df4a3c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/SyncResponseJsonExtensionsTest.kt @@ -1,8 +1,15 @@ package com.x8bit.bitwarden.data.auth.repository.util import com.x8bit.bitwarden.data.auth.repository.model.Organization +import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation +import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization +import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockPolicy +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Test class SyncResponseJsonExtensionsTest { @@ -37,4 +44,46 @@ class SyncResponseJsonExtensionsTest { .toOrganizations(), ) } + + @Test + @OptIn(ExperimentalSerializationApi::class) + fun `policyInformation converts the Json data to policy information`() { + val masterPasswordData = buildJsonObject { + put(key = "minLength", value = 10) + put(key = "minComplexity", value = 3) + put(key = "requireUpper", value = null) + put(key = "requireLower", value = null) + put(key = "requireNumbers", value = true) + put(key = "requireSpecial", value = null) + put(key = "enforceOnLogin", value = true) + } + val masterPasswordPolicy = createMockPolicy( + type = PolicyTypeJson.MASTER_PASSWORD, + data = masterPasswordData, + ) + val policyInformation = PolicyInformation.MasterPassword( + minLength = 10, + minComplexity = 3, + requireUpper = null, + requireLower = null, + requireNumbers = true, + requireSpecial = null, + enforceOnLogin = true, + ) + + assertEquals( + policyInformation, + masterPasswordPolicy.policyInformation, + ) + } + + @Test + fun `policyInformation returns null policy information for null data`() { + val masterPasswordPolicy = createMockPolicy( + type = PolicyTypeJson.MASTER_PASSWORD, + data = null, + ) + + assertNull(masterPasswordPolicy.policyInformation) + } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponsePolicyUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponsePolicyUtil.kt index 4c1336602..94dcf3033 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponsePolicyUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/network/model/SyncResponsePolicyUtil.kt @@ -1,12 +1,19 @@ package com.x8bit.bitwarden.data.vault.datasource.network.model +import kotlinx.serialization.json.JsonObject + /** - * Create a mock [SyncResponseJson.Policy] with a given [number]. + * Create a mock [SyncResponseJson.Policy] with the given [number], [type], and [data]. */ -fun createMockPolicy(number: Int): SyncResponseJson.Policy = +fun createMockPolicy( + number: Int = 1, + type: PolicyTypeJson = PolicyTypeJson.MASTER_PASSWORD, + data: JsonObject? = null, +): SyncResponseJson.Policy = SyncResponseJson.Policy( organizationId = "mockOrganizationId-$number", id = "mockId-$number", - type = PolicyTypeJson.MASTER_PASSWORD, + type = type, isEnabled = false, + data = data, )