mirror of
https://github.com/bitwarden/android.git
synced 2024-11-29 14:28:55 +03:00
BIT-2070: Enable individual Cipher Encryption for SDK (#1148)
This commit is contained in:
parent
3c715d39d6
commit
0d2467d8d2
13 changed files with 144 additions and 43 deletions
|
@ -12,6 +12,10 @@ import com.bitwarden.sdk.ClientPlatform
|
|||
import com.x8bit.bitwarden.data.auth.datasource.sdk.model.PasswordStrength
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toPasswordStrengthOrNull
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toUByte
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Primary implementation of [AuthSdkSource] that serves as a convenience wrapper around a
|
||||
|
@ -20,8 +24,18 @@ import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toUByte
|
|||
class AuthSdkSourceImpl(
|
||||
private val clientAuth: ClientAuth,
|
||||
private val clientPlatform: ClientPlatform,
|
||||
dispatcherManager: DispatcherManager,
|
||||
featureFlagManager: BitwardenFeatureFlagManager,
|
||||
) : AuthSdkSource {
|
||||
|
||||
private val ioScope = CoroutineScope(dispatcherManager.io)
|
||||
|
||||
init {
|
||||
ioScope.launch {
|
||||
clientPlatform.loadFlags(featureFlagManager.featureFlags)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTrustDevice(): Result<TrustDeviceResponse> = runCatching {
|
||||
clientAuth.trustDevice()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.x8bit.bitwarden.data.auth.datasource.sdk.di
|
|||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -20,8 +22,12 @@ object AuthSdkModule {
|
|||
@Singleton
|
||||
fun provideAuthSdkSource(
|
||||
client: Client,
|
||||
featureFlagManager: BitwardenFeatureFlagManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
): AuthSdkSource = AuthSdkSourceImpl(
|
||||
clientAuth = client.auth(),
|
||||
clientPlatform = client.platform(),
|
||||
featureFlagManager = featureFlagManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ interface SdkClientManager {
|
|||
* Returns the cached [Client] instance for the given [userId], otherwise creates and caches
|
||||
* a new one and returns it.
|
||||
*/
|
||||
fun getOrCreateClient(userId: String): Client
|
||||
suspend fun getOrCreateClient(userId: String): Client
|
||||
|
||||
/**
|
||||
* Clears any resources from the [Client] associated with the given [userId] and removes it
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
|
||||
/**
|
||||
* Primary implementation of [SdkClientManager].
|
||||
*/
|
||||
class SdkClientManagerImpl(
|
||||
private val clientProvider: () -> Client = { Client(null) },
|
||||
private val featureFlagManager: BitwardenFeatureFlagManager,
|
||||
private val clientProvider: suspend () -> Client = {
|
||||
Client(null)
|
||||
.apply { platform().loadFlags(featureFlagManager.featureFlags) }
|
||||
},
|
||||
) : SdkClientManager {
|
||||
private val userIdToClientMap = mutableMapOf<String, Client>()
|
||||
|
||||
override fun getOrCreateClient(
|
||||
override suspend fun getOrCreateClient(
|
||||
userId: String,
|
||||
): Client =
|
||||
userIdToClientMap.getOrPut(key = userId) { clientProvider() }
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
|||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManagerImpl
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
@ -97,7 +98,11 @@ object PlatformManagerModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSdkClientManager(): SdkClientManager = SdkClientManagerImpl()
|
||||
fun provideSdkClientManager(
|
||||
featureFlagManager: BitwardenFeatureFlagManager,
|
||||
): SdkClientManager = SdkClientManagerImpl(
|
||||
featureFlagManager = featureFlagManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
/**
|
||||
* Manages the available feature flags for the Bitwarden application.
|
||||
*/
|
||||
interface BitwardenFeatureFlagManager {
|
||||
/**
|
||||
* Returns a map of feature flags.
|
||||
*/
|
||||
val featureFlags: Map<String, Boolean>
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
private const val CIPHER_KEY_ENCRYPTION_KEY = "enableCipherKeyEncryption"
|
||||
|
||||
/**
|
||||
* Primary implementation of [BitwardenFeatureFlagManager].
|
||||
*/
|
||||
class BitwardenFeatureFlagManagerImpl : BitwardenFeatureFlagManager {
|
||||
override val featureFlags: Map<String, Boolean>
|
||||
get() = mapOf(CIPHER_KEY_ENCRYPTION_KEY to true)
|
||||
}
|
|
@ -388,7 +388,7 @@ class VaultSdkSourceImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getClient(
|
||||
private suspend fun getClient(
|
||||
userId: String,
|
||||
): Client = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk.di
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManagerImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSourceImpl
|
||||
import dagger.Module
|
||||
|
@ -24,4 +26,9 @@ object VaultSdkModule {
|
|||
VaultSdkSourceImpl(
|
||||
sdkClientManager = sdkClientManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesBitwardenFeatureFlagManager(): BitwardenFeatureFlagManager =
|
||||
BitwardenFeatureFlagManagerImpl()
|
||||
}
|
||||
|
|
|
@ -10,25 +10,46 @@ import com.bitwarden.crypto.TrustDeviceResponse
|
|||
import com.bitwarden.sdk.ClientAuth
|
||||
import com.bitwarden.sdk.ClientPlatform
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.model.PasswordStrength
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AuthSdkSourceTest {
|
||||
private val clientAuth = mockk<ClientAuth>()
|
||||
private val clientPlatform = mockk<ClientPlatform>()
|
||||
private val clientPlatform = mockk<ClientPlatform> {
|
||||
coEvery { loadFlags(any()) } just runs
|
||||
}
|
||||
private val featureFlagManager = mockk<BitwardenFeatureFlagManager> {
|
||||
coEvery { featureFlags } returns emptyMap()
|
||||
}
|
||||
private val dispatcherManager = FakeDispatcherManager()
|
||||
|
||||
private val authSkdSource: AuthSdkSource = AuthSdkSourceImpl(
|
||||
clientAuth = clientAuth,
|
||||
clientPlatform = clientPlatform,
|
||||
featureFlagManager = featureFlagManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
coVerify(exactly = 1) {
|
||||
featureFlagManager.featureFlags
|
||||
clientPlatform.loadFlags(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getTrustDevice with trustDevice success should return success with correct data`() =
|
||||
runBlocking {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.platform.manager
|
|||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -10,26 +11,28 @@ class SdkClientManagerTest {
|
|||
|
||||
private val sdkClientManager = SdkClientManagerImpl(
|
||||
clientProvider = { mockk(relaxed = true) },
|
||||
featureFlagManager = mockk(),
|
||||
)
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getOrCreateClient should create a new client for each userId and return a cached client for subsequent calls`() {
|
||||
val userId = "userId"
|
||||
val firstClient = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
fun `getOrCreateClient should create a new client for each userId and return a cached client for subsequent calls`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
val firstClient = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
|
||||
// Additional calls for the same userId return the same value
|
||||
val secondClient = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
assertEquals(firstClient, secondClient)
|
||||
// Additional calls for the same userId return the same value
|
||||
val secondClient = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
assertEquals(firstClient, secondClient)
|
||||
|
||||
// Additional calls for different userIds should return different values
|
||||
val otherUserId = "otherUserId"
|
||||
val thirdClient = sdkClientManager.getOrCreateClient(userId = otherUserId)
|
||||
assertNotEquals(firstClient, thirdClient)
|
||||
}
|
||||
// Additional calls for different userIds should return different values
|
||||
val otherUserId = "otherUserId"
|
||||
val thirdClient = sdkClientManager.getOrCreateClient(userId = otherUserId)
|
||||
assertNotEquals(firstClient, thirdClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `destroyClient should call close on the Client and remove it from the cache`() {
|
||||
fun `destroyClient should call close on the Client and remove it from the cache`() = runTest {
|
||||
val userId = "userId"
|
||||
val firstClient = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.sdk
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class BitwardenFeatureFlagManagerTest {
|
||||
|
||||
private val bitwardenFeatureFlagManager = BitwardenFeatureFlagManagerImpl()
|
||||
|
||||
@Test
|
||||
fun `featureFlags should return set feature flags`() {
|
||||
val expected = mapOf("enableCipherKeyEncryption" to true)
|
||||
|
||||
val actual = bitwardenFeatureFlagManager.featureFlags
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ class VaultSdkSourceTest {
|
|||
every { exporters() } returns clientExporters
|
||||
}
|
||||
private val sdkClientManager = mockk<SdkClientManager> {
|
||||
every { getOrCreateClient(any()) } returns client
|
||||
coEvery { getOrCreateClient(any()) } returns client
|
||||
every { destroyClient(any()) } just runs
|
||||
}
|
||||
private val vaultSdkSource: VaultSdkSource = VaultSdkSourceImpl(
|
||||
|
@ -102,7 +102,7 @@ class VaultSdkSourceTest {
|
|||
coVerify {
|
||||
clientCrypto.derivePinKey(pin)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -125,7 +125,7 @@ class VaultSdkSourceTest {
|
|||
coVerify {
|
||||
clientCrypto.derivePinUserKey(encryptedPin = encryptedPin)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -144,7 +144,7 @@ class VaultSdkSourceTest {
|
|||
coVerify {
|
||||
clientCrypto.getUserEncryptionKey()
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -192,7 +192,7 @@ class VaultSdkSourceTest {
|
|||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -218,7 +218,7 @@ class VaultSdkSourceTest {
|
|||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -245,7 +245,7 @@ class VaultSdkSourceTest {
|
|||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -271,7 +271,7 @@ class VaultSdkSourceTest {
|
|||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -297,7 +297,7 @@ class VaultSdkSourceTest {
|
|||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -324,7 +324,7 @@ class VaultSdkSourceTest {
|
|||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -350,7 +350,7 @@ class VaultSdkSourceTest {
|
|||
cipherView = mockCipher,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -376,7 +376,7 @@ class VaultSdkSourceTest {
|
|||
cipher = mockCipher,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -403,7 +403,7 @@ class VaultSdkSourceTest {
|
|||
ciphers = mockCiphers,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -429,7 +429,7 @@ class VaultSdkSourceTest {
|
|||
cipher = mockCiphers,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -455,7 +455,7 @@ class VaultSdkSourceTest {
|
|||
collection = mockCollection,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -482,7 +482,7 @@ class VaultSdkSourceTest {
|
|||
collections = mockCollectionsList,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -509,7 +509,7 @@ class VaultSdkSourceTest {
|
|||
send = mockSend,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -586,7 +586,7 @@ class VaultSdkSourceTest {
|
|||
send = mockSend,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -614,7 +614,7 @@ class VaultSdkSourceTest {
|
|||
folder = mockFolder,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -640,7 +640,7 @@ class VaultSdkSourceTest {
|
|||
folder = mockFolder,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -666,7 +666,7 @@ class VaultSdkSourceTest {
|
|||
folders = mockFolders,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -702,7 +702,7 @@ class VaultSdkSourceTest {
|
|||
decryptedFilePath = "decrypted_path",
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -728,7 +728,7 @@ class VaultSdkSourceTest {
|
|||
passwordHistory = mockPasswordHistoryView,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -754,7 +754,7 @@ class VaultSdkSourceTest {
|
|||
list = mockPasswordHistoryList,
|
||||
)
|
||||
}
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -781,7 +781,7 @@ class VaultSdkSourceTest {
|
|||
)
|
||||
}
|
||||
|
||||
verify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
coVerify { sdkClientManager.getOrCreateClient(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in a new issue