Skip to content

Commit 7b94daf

Browse files
Chore: Update Policy Types to conform to known values (#6965)
1 parent a5f7288 commit 7b94daf

6 files changed

Lines changed: 211 additions & 9 deletions

File tree

app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.datasource.disk
22

33
import android.content.SharedPreferences
44
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
5+
import com.bitwarden.core.data.serializer.SafeMapSerializer
56
import com.bitwarden.core.data.util.decodeFromStringOrNull
67
import com.bitwarden.data.datasource.disk.BaseEncryptedDiskSource
78
import com.bitwarden.network.model.AccountKeysJson
@@ -14,6 +15,7 @@ import com.x8bit.bitwarden.data.platform.datasource.disk.legacy.LegacySecureStor
1415
import kotlinx.coroutines.flow.Flow
1516
import kotlinx.coroutines.flow.MutableSharedFlow
1617
import kotlinx.coroutines.flow.onSubscription
18+
import kotlinx.serialization.builtins.serializer
1719
import kotlinx.serialization.json.Json
1820
import java.time.Instant
1921
import java.util.UUID
@@ -485,8 +487,13 @@ class AuthDiskSourceImpl(
485487
getString(key = POLICIES_KEY.appendIdentifier(userId))
486488
?.let {
487489
// The policies are stored as a map.
488-
val policiesMap: Map<String, SyncResponseJson.Policy>? =
489-
json.decodeFromStringOrNull(it)
490+
val policiesMap = json.decodeFromStringOrNull(
491+
deserializer = SafeMapSerializer(
492+
keySerializer = String.serializer(),
493+
valueSerializer = SyncResponseJson.Policy.serializer(),
494+
),
495+
string = it,
496+
)
490497
policiesMap?.values?.toList()
491498
}
492499

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.bitwarden.core.data.serializer
2+
3+
import com.bitwarden.core.data.util.decodeFromJsonElementOrNull
4+
import kotlinx.serialization.KSerializer
5+
import kotlinx.serialization.builtins.MapSerializer
6+
import kotlinx.serialization.descriptors.SerialDescriptor
7+
import kotlinx.serialization.encoding.Decoder
8+
import kotlinx.serialization.encoding.Encoder
9+
import kotlinx.serialization.json.JsonDecoder
10+
import kotlinx.serialization.json.JsonPrimitive
11+
import kotlinx.serialization.json.jsonObject
12+
13+
/**
14+
* A [KSerializer] for parsing a map of items and allows for the individual items in the object
15+
* to fail when deserialized without returning an error. If any number of items fail to be parsed,
16+
* they are removed from the map.
17+
*/
18+
class SafeMapSerializer<K, V>(
19+
private val keySerializer: KSerializer<K>,
20+
private val valueSerializer: KSerializer<V>,
21+
) : KSerializer<Map<K, V>> {
22+
private val innerSerializer: KSerializer<Map<K, V>> = MapSerializer(
23+
keySerializer = keySerializer,
24+
valueSerializer = valueSerializer,
25+
)
26+
override val descriptor: SerialDescriptor = innerSerializer.descriptor
27+
28+
override fun serialize(encoder: Encoder, value: Map<K, V>) {
29+
innerSerializer.serialize(encoder, value)
30+
}
31+
32+
override fun deserialize(
33+
decoder: Decoder,
34+
): Map<K, V> = with(decoder as JsonDecoder) {
35+
decodeJsonElement()
36+
.jsonObject
37+
.mapNotNull { (keyString, valueElement) ->
38+
val key = json
39+
.decodeFromJsonElementOrNull(
40+
deserializer = keySerializer,
41+
element = JsonPrimitive(value = keyString),
42+
)
43+
?: return@mapNotNull null
44+
val value = json
45+
.decodeFromJsonElementOrNull(
46+
deserializer = valueSerializer,
47+
element = valueElement,
48+
)
49+
?: return@mapNotNull null
50+
key to value
51+
}
52+
.toMap()
53+
}
54+
}

core/src/main/kotlin/com/bitwarden/core/data/util/JsonExtensions.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ inline fun <reified T> Json.decodeFromStringOrNull(
3636
null
3737
}
3838

39+
/**
40+
* Attempts to decode the given JSON [string] into the given type [T]. If there is an error in
41+
* processing the JSON or deserializing it to an instance of [T], `null` will be returned.
42+
*/
43+
inline fun <reified T> Json.decodeFromStringOrNull(
44+
deserializer: DeserializationStrategy<T>,
45+
string: String,
46+
): T? =
47+
try {
48+
decodeFromString(string = string, deserializer = deserializer)
49+
} catch (_: SerializationException) {
50+
null
51+
} catch (_: IllegalArgumentException) {
52+
null
53+
}
54+
3955
/**
4056
* Attempts to decode the given JSON [string] into the given type [T]. If there is an error in
4157
* processing the JSON or deserializing, the exception is still throw after [onFailure] lambda is
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.bitwarden.core.data.serializer
2+
3+
import com.bitwarden.core.di.CoreModule
4+
import io.mockk.mockk
5+
import kotlinx.serialization.SerialName
6+
import kotlinx.serialization.Serializable
7+
import kotlinx.serialization.json.buildJsonObject
8+
import kotlinx.serialization.json.put
9+
import kotlinx.serialization.json.putJsonObject
10+
import org.junit.jupiter.api.Assertions.assertEquals
11+
import org.junit.jupiter.api.Test
12+
13+
class SafeMapSerializerTest {
14+
private val json = CoreModule.providesJson(buildInfoManager = mockk(relaxed = true))
15+
16+
@Test
17+
fun `deserializes JSON successfully with invalid items`() {
18+
assertEquals(
19+
SampleClass(data = mapOf("foo" to SampleClass.InnerSampleClass(string = "test"))),
20+
json.decodeFromString<SampleClass>(
21+
"""
22+
{
23+
"data": {
24+
"bar": {
25+
"string": 5
26+
},
27+
"foo": {
28+
"string": "test"
29+
}
30+
}
31+
}
32+
""",
33+
),
34+
)
35+
}
36+
37+
@Test
38+
fun `deserializes JSON successfully with null list`() {
39+
assertEquals(
40+
SampleClass(data = null),
41+
json.decodeFromString<SampleClass>(
42+
"""
43+
{
44+
"data": null
45+
}
46+
""",
47+
),
48+
)
49+
}
50+
51+
@Test
52+
fun `serialized JSON successfully`() {
53+
assertEquals(
54+
buildJsonObject {
55+
putJsonObject(key = "data") {
56+
putJsonObject("foo") {
57+
put(key = "string", value = "test")
58+
}
59+
}
60+
},
61+
json.encodeToJsonElement(
62+
serializer = SampleClass.serializer(),
63+
value = SampleClass(
64+
data = mapOf("foo" to SampleClass.InnerSampleClass(string = "test")),
65+
),
66+
),
67+
)
68+
}
69+
}
70+
71+
@Serializable
72+
private data class SampleClass(
73+
@Serializable(with = SafeMapSerializer::class)
74+
@SerialName("data")
75+
val data: Map<String, InnerSampleClass>?,
76+
) {
77+
@Serializable
78+
data class InnerSampleClass(
79+
@SerialName("string")
80+
val string: String,
81+
)
82+
}

network/src/main/kotlin/com/bitwarden/network/model/PolicyTypeJson.kt

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,23 @@ enum class PolicyTypeJson {
7777
DISABLE_PERSONAL_VAULT_EXPORT,
7878

7979
/**
80-
* Activate the auto-fill in the browser extension. Currently unused in mobile.
80+
* Activate the autofill in the browser extension. Currently unused in mobile.
8181
*/
8282
@SerialName("11")
8383
ACTIVATE_AUTOFILL,
8484

85+
/**
86+
* Automatically logs members into apps using single sign-on.
87+
*/
88+
@SerialName("12")
89+
AUTOMATIC_APP_LOG_IN,
90+
91+
/**
92+
* Removes members' access to the free Bitwarden Families sponsorship benefit.
93+
*/
94+
@SerialName("13")
95+
FREE_FAMILIES_SPONSORSHIP_POLICY,
96+
8597
/**
8698
* Hides the setting to "Unlock with Pin".
8799
*/
@@ -95,18 +107,48 @@ enum class PolicyTypeJson {
95107
RESTRICT_ITEM_TYPES,
96108

97109
/**
98-
* Represents an unknown policy type.
110+
* Sets the default URI match detection strategy for autofill.
111+
*/
112+
@SerialName("16")
113+
URI_MATCH_DEFAULTS,
114+
115+
/**
116+
* Sets the default behavior for the autotype feature.
117+
*/
118+
@SerialName("17")
119+
AUTOTYPE_DEFAULT_SETTING,
120+
121+
/**
122+
* Automatically confirms invited users into the organization.
123+
*/
124+
@SerialName("18")
125+
AUTOMATIC_USER_CONFIRMATION,
126+
127+
/**
128+
* Blocks account creation for users with email addresses on claimed domains.
129+
*/
130+
@SerialName("19")
131+
BLOCK_CLAIMED_DOMAIN_ACCOUNT_CREATION,
132+
133+
/**
134+
* Displays an organization-configured banner message to members in their vault.
135+
*/
136+
@SerialName("20")
137+
ORGANIZATION_USER_NOTIFICATION,
138+
139+
/**
140+
* Configures Send-related behavior: disabling Sends, email visibility, access controls,
141+
* Send types, and deletion.
99142
*
100-
* This is used for forward compatibility to handle new policy types that the client doesn't yet
101-
* understand.
143+
* Supersedes [`DisableSend`](Self::DisableSend) and [`SendOptions`](Self::SendOptions) when
144+
* the `pm-31885-send-controls` feature flag is active on the server.
102145
*/
103-
@SerialName("-1")
104-
UNKNOWN,
146+
@SerialName("21")
147+
SEND_CONTROLS,
105148
}
106149

107150
@Keep
108151
private class PolicyTypeSerializer : BaseEnumeratedIntSerializer<PolicyTypeJson>(
109152
className = "PolicyTypeJson",
110153
values = PolicyTypeJson.entries.toTypedArray(),
111-
default = PolicyTypeJson.UNKNOWN,
112154
)

network/src/main/kotlin/com/bitwarden/network/model/SyncResponseJson.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ data class SyncResponseJson(
4040
@SerialName("ciphers")
4141
val ciphers: List<Cipher>?,
4242

43+
@Contextual
4344
@SerialName("policies")
4445
val policies: List<Policy>?,
4546

0 commit comments

Comments
 (0)