Skip to content

Commit d3f5621

Browse files
Revert "PM-34193: Rollback SDK update for Vault lockout bug" (#6725)
1 parent 5ce64c3 commit d3f5621

6 files changed

Lines changed: 228 additions & 1 deletion

File tree

app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/sdk/SdkRepositoryFactoryImpl.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.bitwarden.sdk.ServerCommunicationConfigRepository
77
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
88
import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource
99
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.SdkCipherRepository
10+
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.SdkLocalUserDataKeyStateRepository
1011
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.SdkTokenRepository
1112
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.ServerCommunicationConfigRepositoryImpl
1213
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
@@ -25,6 +26,10 @@ class SdkRepositoryFactoryImpl(
2526
cipher = getSdkRepository(userId = userId),
2627
folder = null,
2728
userKeyState = null,
29+
localUserDataKeyState = SdkLocalUserDataKeyStateRepository(
30+
authDiskSource = authDiskSource,
31+
),
32+
ephemeralPinEnvelopeState = null,
2833
)
2934

3035
override fun getClientManagedTokens(
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.x8bit.bitwarden.data.platform.manager.sdk.repository
2+
3+
import com.bitwarden.core.LocalUserDataKeyState
4+
import com.bitwarden.sdk.LocalUserDataKeyStateRepository
5+
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
6+
7+
/**
8+
* An implementation of a Bitwarden SDK [LocalUserDataKeyStateRepository].
9+
*/
10+
class SdkLocalUserDataKeyStateRepository(
11+
private val authDiskSource: AuthDiskSource,
12+
) : LocalUserDataKeyStateRepository {
13+
override suspend fun get(id: String): LocalUserDataKeyState? {
14+
return authDiskSource
15+
.getLocalUserDataKey(userId = id)
16+
?.let { LocalUserDataKeyState(wrappedKey = it) }
17+
}
18+
19+
override suspend fun has(
20+
id: String,
21+
): Boolean = authDiskSource.getLocalUserDataKey(userId = id) != null
22+
23+
override suspend fun list(): List<LocalUserDataKeyState> =
24+
authDiskSource
25+
.userState
26+
?.accounts
27+
?.mapNotNull { get(id = it.key) }
28+
.orEmpty()
29+
30+
override suspend fun remove(id: String) {
31+
authDiskSource.storeLocalUserDataKey(userId = id, wrappedKey = null)
32+
}
33+
34+
override suspend fun removeAll() {
35+
removeBulk(keys = authDiskSource.userState?.accounts.orEmpty().keys.toList())
36+
}
37+
38+
override suspend fun removeBulk(keys: List<String>) {
39+
keys.forEach { remove(id = it) }
40+
}
41+
42+
override suspend fun set(id: String, value: LocalUserDataKeyState) {
43+
authDiskSource.storeLocalUserDataKey(userId = id, value.wrappedKey)
44+
}
45+
46+
override suspend fun setBulk(values: Map<String, LocalUserDataKeyState>) {
47+
values.forEach { (id, value) -> set(id = id, value = value) }
48+
}
49+
}

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ class VaultLockManagerImpl(
711711
is InitUserCryptoMethod.DecryptedKey,
712712
is InitUserCryptoMethod.DeviceKey,
713713
is InitUserCryptoMethod.KeyConnector,
714+
is InitUserCryptoMethod.KeyConnectorUrl,
714715
is InitUserCryptoMethod.Pin,
715716
is InitUserCryptoMethod.PinEnvelope,
716717
-> return

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/InitUserCryptoMethodExtensions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ val InitUserCryptoMethod.logTag: String
1414
is InitUserCryptoMethod.KeyConnector -> "Key Connector"
1515
is InitUserCryptoMethod.Pin -> "Pin"
1616
is InitUserCryptoMethod.PinEnvelope -> "Pin Envelope"
17+
is InitUserCryptoMethod.KeyConnectorUrl -> "Key Connector Url"
1718
is InitUserCryptoMethod.MasterPasswordUnlock -> "Master Password Unlock"
1819
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package com.x8bit.bitwarden.data.platform.manager.sdk.repository
2+
3+
import com.bitwarden.core.LocalUserDataKeyState
4+
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
5+
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
6+
import io.mockk.mockk
7+
import kotlinx.coroutines.test.runTest
8+
import org.junit.jupiter.api.Assertions.assertEquals
9+
import org.junit.jupiter.api.Assertions.assertFalse
10+
import org.junit.jupiter.api.Assertions.assertNull
11+
import org.junit.jupiter.api.Assertions.assertTrue
12+
import org.junit.jupiter.api.Test
13+
14+
class SdkLocalUserDataKeyStateRepositoryTest {
15+
16+
private val fakeAuthDiskSource = FakeAuthDiskSource()
17+
18+
private val repository = SdkLocalUserDataKeyStateRepository(
19+
authDiskSource = fakeAuthDiskSource,
20+
)
21+
22+
@Test
23+
fun `get should return null when no key is stored for the given id`() = runTest {
24+
assertNull(repository.get(id = USER_ID))
25+
}
26+
27+
@Test
28+
fun `get should return LocalUserDataKeyState when key is stored for the given id`() = runTest {
29+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
30+
31+
assertEquals(
32+
LocalUserDataKeyState(wrappedKey = WRAPPED_KEY),
33+
repository.get(id = USER_ID),
34+
)
35+
}
36+
37+
@Test
38+
fun `has should return false when no key is stored for the given id`() = runTest {
39+
assertFalse(repository.has(id = USER_ID))
40+
}
41+
42+
@Test
43+
fun `has should return true when a key is stored for the given id`() = runTest {
44+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
45+
46+
assertTrue(repository.has(id = USER_ID))
47+
}
48+
49+
@Test
50+
fun `list should return empty list when userState is null`() = runTest {
51+
fakeAuthDiskSource.userState = null
52+
53+
assertEquals(emptyList<LocalUserDataKeyState>(), repository.list())
54+
}
55+
56+
@Test
57+
fun `list should return empty list when no keys are stored for any account`() = runTest {
58+
fakeAuthDiskSource.userState = UserStateJson(
59+
activeUserId = USER_ID,
60+
accounts = mapOf(USER_ID to mockk()),
61+
)
62+
63+
assertEquals(emptyList<LocalUserDataKeyState>(), repository.list())
64+
}
65+
66+
@Test
67+
fun `list should return LocalUserDataKeyState for each account that has a stored key`() =
68+
runTest {
69+
fakeAuthDiskSource.userState = UserStateJson(
70+
activeUserId = USER_ID,
71+
accounts = mapOf(
72+
USER_ID to mockk(),
73+
USER_ID_2 to mockk(),
74+
),
75+
)
76+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
77+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID_2, wrappedKey = WRAPPED_KEY_2)
78+
79+
assertEquals(
80+
listOf(
81+
LocalUserDataKeyState(wrappedKey = WRAPPED_KEY),
82+
LocalUserDataKeyState(wrappedKey = WRAPPED_KEY_2),
83+
),
84+
repository.list(),
85+
)
86+
}
87+
88+
@Test
89+
fun `list should omit accounts that have no stored key`() = runTest {
90+
fakeAuthDiskSource.userState = UserStateJson(
91+
activeUserId = USER_ID,
92+
accounts = mapOf(USER_ID to mockk(), USER_ID_2 to mockk()),
93+
)
94+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
95+
96+
assertEquals(
97+
listOf(LocalUserDataKeyState(wrappedKey = WRAPPED_KEY)),
98+
repository.list(),
99+
)
100+
}
101+
102+
@Test
103+
fun `remove should clear the stored key for the given id`() = runTest {
104+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
105+
106+
repository.remove(id = USER_ID)
107+
108+
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
109+
}
110+
111+
@Test
112+
fun `removeAll should clear the stored key for all accounts`() = runTest {
113+
fakeAuthDiskSource.userState = UserStateJson(
114+
activeUserId = USER_ID,
115+
accounts = mapOf(
116+
USER_ID to mockk(),
117+
USER_ID_2 to mockk(),
118+
),
119+
)
120+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
121+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID_2, wrappedKey = WRAPPED_KEY_2)
122+
123+
repository.removeAll()
124+
125+
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
126+
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID_2))
127+
}
128+
129+
@Test
130+
fun `removeAll should do nothing when userState is null`() = runTest {
131+
fakeAuthDiskSource.userState = null
132+
133+
repository.removeAll()
134+
}
135+
136+
@Test
137+
fun `removeBulk should clear the stored key for each given id`() = runTest {
138+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
139+
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID_2, wrappedKey = WRAPPED_KEY_2)
140+
141+
repository.removeBulk(keys = listOf(USER_ID, USER_ID_2))
142+
143+
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
144+
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID_2))
145+
}
146+
147+
@Test
148+
fun `set should store the wrapped key for the given id`() = runTest {
149+
repository.set(id = USER_ID, value = LocalUserDataKeyState(wrappedKey = WRAPPED_KEY))
150+
151+
assertEquals(WRAPPED_KEY, fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
152+
}
153+
154+
@Test
155+
fun `setBulk should store the wrapped key for each given id`() = runTest {
156+
repository.setBulk(
157+
values = mapOf(
158+
USER_ID to LocalUserDataKeyState(wrappedKey = WRAPPED_KEY),
159+
USER_ID_2 to LocalUserDataKeyState(wrappedKey = WRAPPED_KEY_2),
160+
),
161+
)
162+
163+
assertEquals(WRAPPED_KEY, fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
164+
assertEquals(WRAPPED_KEY_2, fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID_2))
165+
}
166+
}
167+
168+
private const val USER_ID: String = "userId"
169+
private const val USER_ID_2: String = "userId2"
170+
private const val WRAPPED_KEY: String = "wrappedKey"
171+
private const val WRAPPED_KEY_2: String = "wrappedKey2"

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ androidxRoom = "2.8.4"
3030
androidxSecurityCrypto = "1.1.0"
3131
androidxSplash = "1.2.0"
3232
androidxWork = "2.11.1"
33-
bitwardenSdk = "2.0.0-5451-c73f9161"
33+
bitwardenSdk = "2.0.0-5676-14521973"
3434
crashlytics = "3.0.6"
3535
detekt = "1.23.8"
3636
firebaseBom = "34.10.0"

0 commit comments

Comments
 (0)