PM-34193: Rollback SDK update for Vault lockout bug (#6724)

This commit is contained in:
David Perez
2026-03-27 07:52:52 -05:00
committed by GitHub
parent 6cf15fb792
commit 5ce64c3a47
6 changed files with 1 additions and 228 deletions

View File

@@ -7,7 +7,6 @@ import com.bitwarden.sdk.ServerCommunicationConfigRepository
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.SdkCipherRepository
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.SdkLocalUserDataKeyStateRepository
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.SdkTokenRepository
import com.x8bit.bitwarden.data.platform.manager.sdk.repository.ServerCommunicationConfigRepositoryImpl
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
@@ -26,10 +25,6 @@ class SdkRepositoryFactoryImpl(
cipher = getSdkRepository(userId = userId),
folder = null,
userKeyState = null,
localUserDataKeyState = SdkLocalUserDataKeyStateRepository(
authDiskSource = authDiskSource,
),
ephemeralPinEnvelopeState = null,
)
override fun getClientManagedTokens(

View File

@@ -1,49 +0,0 @@
package com.x8bit.bitwarden.data.platform.manager.sdk.repository
import com.bitwarden.core.LocalUserDataKeyState
import com.bitwarden.sdk.LocalUserDataKeyStateRepository
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
/**
* An implementation of a Bitwarden SDK [LocalUserDataKeyStateRepository].
*/
class SdkLocalUserDataKeyStateRepository(
private val authDiskSource: AuthDiskSource,
) : LocalUserDataKeyStateRepository {
override suspend fun get(id: String): LocalUserDataKeyState? {
return authDiskSource
.getLocalUserDataKey(userId = id)
?.let { LocalUserDataKeyState(wrappedKey = it) }
}
override suspend fun has(
id: String,
): Boolean = authDiskSource.getLocalUserDataKey(userId = id) != null
override suspend fun list(): List<LocalUserDataKeyState> =
authDiskSource
.userState
?.accounts
?.mapNotNull { get(id = it.key) }
.orEmpty()
override suspend fun remove(id: String) {
authDiskSource.storeLocalUserDataKey(userId = id, wrappedKey = null)
}
override suspend fun removeAll() {
removeBulk(keys = authDiskSource.userState?.accounts.orEmpty().keys.toList())
}
override suspend fun removeBulk(keys: List<String>) {
keys.forEach { remove(id = it) }
}
override suspend fun set(id: String, value: LocalUserDataKeyState) {
authDiskSource.storeLocalUserDataKey(userId = id, value.wrappedKey)
}
override suspend fun setBulk(values: Map<String, LocalUserDataKeyState>) {
values.forEach { (id, value) -> set(id = id, value = value) }
}
}

View File

@@ -711,7 +711,6 @@ class VaultLockManagerImpl(
is InitUserCryptoMethod.DecryptedKey,
is InitUserCryptoMethod.DeviceKey,
is InitUserCryptoMethod.KeyConnector,
is InitUserCryptoMethod.KeyConnectorUrl,
is InitUserCryptoMethod.Pin,
is InitUserCryptoMethod.PinEnvelope,
-> return

View File

@@ -14,6 +14,5 @@ val InitUserCryptoMethod.logTag: String
is InitUserCryptoMethod.KeyConnector -> "Key Connector"
is InitUserCryptoMethod.Pin -> "Pin"
is InitUserCryptoMethod.PinEnvelope -> "Pin Envelope"
is InitUserCryptoMethod.KeyConnectorUrl -> "Key Connector Url"
is InitUserCryptoMethod.MasterPasswordUnlock -> "Master Password Unlock"
}

View File

@@ -1,171 +0,0 @@
package com.x8bit.bitwarden.data.platform.manager.sdk.repository
import com.bitwarden.core.LocalUserDataKeyState
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
class SdkLocalUserDataKeyStateRepositoryTest {
private val fakeAuthDiskSource = FakeAuthDiskSource()
private val repository = SdkLocalUserDataKeyStateRepository(
authDiskSource = fakeAuthDiskSource,
)
@Test
fun `get should return null when no key is stored for the given id`() = runTest {
assertNull(repository.get(id = USER_ID))
}
@Test
fun `get should return LocalUserDataKeyState when key is stored for the given id`() = runTest {
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
assertEquals(
LocalUserDataKeyState(wrappedKey = WRAPPED_KEY),
repository.get(id = USER_ID),
)
}
@Test
fun `has should return false when no key is stored for the given id`() = runTest {
assertFalse(repository.has(id = USER_ID))
}
@Test
fun `has should return true when a key is stored for the given id`() = runTest {
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
assertTrue(repository.has(id = USER_ID))
}
@Test
fun `list should return empty list when userState is null`() = runTest {
fakeAuthDiskSource.userState = null
assertEquals(emptyList<LocalUserDataKeyState>(), repository.list())
}
@Test
fun `list should return empty list when no keys are stored for any account`() = runTest {
fakeAuthDiskSource.userState = UserStateJson(
activeUserId = USER_ID,
accounts = mapOf(USER_ID to mockk()),
)
assertEquals(emptyList<LocalUserDataKeyState>(), repository.list())
}
@Test
fun `list should return LocalUserDataKeyState for each account that has a stored key`() =
runTest {
fakeAuthDiskSource.userState = UserStateJson(
activeUserId = USER_ID,
accounts = mapOf(
USER_ID to mockk(),
USER_ID_2 to mockk(),
),
)
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID_2, wrappedKey = WRAPPED_KEY_2)
assertEquals(
listOf(
LocalUserDataKeyState(wrappedKey = WRAPPED_KEY),
LocalUserDataKeyState(wrappedKey = WRAPPED_KEY_2),
),
repository.list(),
)
}
@Test
fun `list should omit accounts that have no stored key`() = runTest {
fakeAuthDiskSource.userState = UserStateJson(
activeUserId = USER_ID,
accounts = mapOf(USER_ID to mockk(), USER_ID_2 to mockk()),
)
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
assertEquals(
listOf(LocalUserDataKeyState(wrappedKey = WRAPPED_KEY)),
repository.list(),
)
}
@Test
fun `remove should clear the stored key for the given id`() = runTest {
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
repository.remove(id = USER_ID)
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
}
@Test
fun `removeAll should clear the stored key for all accounts`() = runTest {
fakeAuthDiskSource.userState = UserStateJson(
activeUserId = USER_ID,
accounts = mapOf(
USER_ID to mockk(),
USER_ID_2 to mockk(),
),
)
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID_2, wrappedKey = WRAPPED_KEY_2)
repository.removeAll()
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID_2))
}
@Test
fun `removeAll should do nothing when userState is null`() = runTest {
fakeAuthDiskSource.userState = null
repository.removeAll()
}
@Test
fun `removeBulk should clear the stored key for each given id`() = runTest {
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID, wrappedKey = WRAPPED_KEY)
fakeAuthDiskSource.storeLocalUserDataKey(userId = USER_ID_2, wrappedKey = WRAPPED_KEY_2)
repository.removeBulk(keys = listOf(USER_ID, USER_ID_2))
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
assertNull(fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID_2))
}
@Test
fun `set should store the wrapped key for the given id`() = runTest {
repository.set(id = USER_ID, value = LocalUserDataKeyState(wrappedKey = WRAPPED_KEY))
assertEquals(WRAPPED_KEY, fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
}
@Test
fun `setBulk should store the wrapped key for each given id`() = runTest {
repository.setBulk(
values = mapOf(
USER_ID to LocalUserDataKeyState(wrappedKey = WRAPPED_KEY),
USER_ID_2 to LocalUserDataKeyState(wrappedKey = WRAPPED_KEY_2),
),
)
assertEquals(WRAPPED_KEY, fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID))
assertEquals(WRAPPED_KEY_2, fakeAuthDiskSource.getLocalUserDataKey(userId = USER_ID_2))
}
}
private const val USER_ID: String = "userId"
private const val USER_ID_2: String = "userId2"
private const val WRAPPED_KEY: String = "wrappedKey"
private const val WRAPPED_KEY_2: String = "wrappedKey2"

View File

@@ -30,7 +30,7 @@ androidxRoom = "2.8.4"
androidxSecurityCrypto = "1.1.0"
androidxSplash = "1.2.0"
androidxWork = "2.11.1"
bitwardenSdk = "2.0.0-5676-14521973"
bitwardenSdk = "2.0.0-5451-c73f9161"
crashlytics = "3.0.6"
detekt = "1.23.8"
firebaseBom = "34.10.0"