mirror of
https://github.com/bitwarden/android.git
synced 2026-06-01 18:26:31 -05:00
BIT-990: Initialize Crypto for Vault (#213)
This commit is contained in:
@@ -31,6 +31,8 @@ import com.x8bit.bitwarden.data.auth.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
@@ -45,12 +47,14 @@ import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("LargeClass")
|
||||
class AuthRepositoryTest {
|
||||
|
||||
private val dispatcherManager: DispatcherManager = FakeDispatcherManager()
|
||||
private val accountsService: AccountsService = mockk()
|
||||
private val identityService: IdentityService = mockk()
|
||||
private val haveIBeenPwnedService: HaveIBeenPwnedService = mockk()
|
||||
private val vaultRepository: VaultRepository = mockk()
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val authSdkSource = mockk<AuthSdkSource> {
|
||||
coEvery {
|
||||
@@ -85,6 +89,7 @@ class AuthRepositoryTest {
|
||||
authSdkSource = authSdkSource,
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
dispatcherManager = dispatcherManager,
|
||||
vaultRepository = vaultRepository,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
@@ -183,7 +188,8 @@ class AuthRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login get token succeeds should return Success and update AuthState and stored keys`() =
|
||||
@Suppress("MaxLineLength")
|
||||
fun `login get token succeeds should return Success, update AuthState, update stored keys, and unlockVaultAndSync`() =
|
||||
runTest {
|
||||
val successResponse = GET_TOKEN_RESPONSE_SUCCESS
|
||||
coEvery {
|
||||
@@ -197,6 +203,9 @@ class AuthRepositoryTest {
|
||||
)
|
||||
}
|
||||
.returns(Result.success(successResponse))
|
||||
coEvery {
|
||||
vaultRepository.unlockVaultAndSync(masterPassword = PASSWORD)
|
||||
} returns VaultUnlockResult.Success
|
||||
every {
|
||||
GET_TOKEN_RESPONSE_SUCCESS.toUserState(previousUserState = null)
|
||||
} returns SINGLE_USER_STATE_1
|
||||
@@ -219,6 +228,9 @@ class AuthRepositoryTest {
|
||||
captchaToken = null,
|
||||
)
|
||||
}
|
||||
coVerify {
|
||||
vaultRepository.unlockVaultAndSync(masterPassword = PASSWORD)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -597,6 +609,9 @@ class AuthRepositoryTest {
|
||||
captchaToken = null,
|
||||
)
|
||||
} returns Result.success(successResponse)
|
||||
coEvery {
|
||||
vaultRepository.unlockVaultAndSync(masterPassword = PASSWORD)
|
||||
} returns VaultUnlockResult.Success
|
||||
every {
|
||||
GET_TOKEN_RESPONSE_SUCCESS.toUserState(previousUserState = null)
|
||||
} returns SINGLE_USER_STATE_1
|
||||
@@ -643,6 +658,9 @@ class AuthRepositoryTest {
|
||||
captchaToken = null,
|
||||
)
|
||||
} returns Result.success(successResponse)
|
||||
coEvery {
|
||||
vaultRepository.unlockVaultAndSync(masterPassword = PASSWORD)
|
||||
} returns VaultUnlockResult.Success
|
||||
every {
|
||||
GET_TOKEN_RESPONSE_SUCCESS.toUserState(previousUserState = SINGLE_USER_STATE_2)
|
||||
} returns MULTI_USER_STATE
|
||||
|
||||
@@ -5,22 +5,99 @@ import com.bitwarden.core.CipherListView
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.Folder
|
||||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.bitwarden.sdk.BitwardenException
|
||||
import com.bitwarden.sdk.ClientCrypto
|
||||
import com.bitwarden.sdk.ClientVault
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.IllegalStateException
|
||||
|
||||
class VaultSdkSourceTest {
|
||||
private val clientVault = mockk<ClientVault>()
|
||||
|
||||
private val clientCrypto = mockk<ClientCrypto>()
|
||||
private val vaultSdkSource: VaultSdkSource = VaultSdkSourceImpl(
|
||||
clientVault = clientVault,
|
||||
clientCrypto = clientCrypto,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `initializeCrypto with sdk success should return InitializeCryptoResult Success`() =
|
||||
runBlocking {
|
||||
val mockInitCryptoRequest = mockk<InitCryptoRequest>()
|
||||
coEvery {
|
||||
clientCrypto.initializeCrypto(
|
||||
req = mockInitCryptoRequest,
|
||||
)
|
||||
} returns Unit
|
||||
val result = vaultSdkSource.initializeCrypto(
|
||||
request = mockInitCryptoRequest,
|
||||
)
|
||||
assertEquals(
|
||||
InitializeCryptoResult.Success.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientCrypto.initializeCrypto(
|
||||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initializeCrypto with sdk failure should return failure`() = runBlocking {
|
||||
val mockInitCryptoRequest = mockk<InitCryptoRequest>()
|
||||
val expectedException = IllegalStateException("mock")
|
||||
coEvery {
|
||||
clientCrypto.initializeCrypto(
|
||||
req = mockInitCryptoRequest,
|
||||
)
|
||||
} throws expectedException
|
||||
val result = vaultSdkSource.initializeCrypto(
|
||||
request = mockInitCryptoRequest,
|
||||
)
|
||||
assertEquals(
|
||||
expectedException.asFailure(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientCrypto.initializeCrypto(
|
||||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initializeCrypto with BitwardenException failure should return AuthenticationError`() =
|
||||
runBlocking {
|
||||
val mockInitCryptoRequest = mockk<InitCryptoRequest>()
|
||||
val expectedException = BitwardenException.E(message = "")
|
||||
coEvery {
|
||||
clientCrypto.initializeCrypto(
|
||||
req = mockInitCryptoRequest,
|
||||
)
|
||||
} throws expectedException
|
||||
val result = vaultSdkSource.initializeCrypto(
|
||||
request = mockInitCryptoRequest,
|
||||
)
|
||||
assertEquals(
|
||||
InitializeCryptoResult.AuthenticationError.asSuccess(),
|
||||
result,
|
||||
)
|
||||
coVerify {
|
||||
clientCrypto.initializeCrypto(
|
||||
req = mockInitCryptoRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Cipher decrypt should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val mockCipher = mockk<Cipher>()
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository
|
||||
|
||||
import com.bitwarden.core.InitCryptoRequest
|
||||
import com.bitwarden.core.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSyncResponse
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.createMockSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultRepositoryTest {
|
||||
@@ -50,6 +57,182 @@ class VaultRepositoryTest {
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with initializeCrypto Success should sync and return Success`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns mockk()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockk()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
request = InitCryptoRequest(
|
||||
kdfParams = Kdf.Pbkdf2(iterations = DEFAULT_PBKDF2_ITERATIONS.toUInt()),
|
||||
email = "email",
|
||||
password = "mockPassword-1",
|
||||
userKey = "mockKey-1",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
organizationKeys = mapOf(),
|
||||
),
|
||||
)
|
||||
} returns Result.success(InitializeCryptoResult.Success)
|
||||
|
||||
val result = vaultRepository.unlockVaultAndSync(masterPassword = "mockPassword-1")
|
||||
|
||||
assertEquals(
|
||||
VaultUnlockResult.Success,
|
||||
result,
|
||||
)
|
||||
coVerify { syncService.sync() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with initializeCrypto failure should return GenericError`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns mockk()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockk()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
request = InitCryptoRequest(
|
||||
kdfParams = Kdf.Pbkdf2(iterations = DEFAULT_PBKDF2_ITERATIONS.toUInt()),
|
||||
email = "email",
|
||||
password = "mockPassword-1",
|
||||
userKey = "mockKey-1",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
organizationKeys = mapOf(),
|
||||
),
|
||||
)
|
||||
} returns Result.failure(IllegalStateException())
|
||||
|
||||
val result = vaultRepository.unlockVaultAndSync(masterPassword = "mockPassword-1")
|
||||
|
||||
assertEquals(
|
||||
VaultUnlockResult.GenericError,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVaultAndSync with initializeCrypto AuthenticationError should return AuthenticationError`() =
|
||||
runTest {
|
||||
coEvery { syncService.sync() } returns Result.success(createMockSyncResponse(number = 1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns mockk()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns mockk()
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
request = InitCryptoRequest(
|
||||
kdfParams = Kdf.Pbkdf2(iterations = DEFAULT_PBKDF2_ITERATIONS.toUInt()),
|
||||
email = "email",
|
||||
password = "",
|
||||
userKey = "mockKey-1",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
organizationKeys = mapOf(),
|
||||
),
|
||||
)
|
||||
} returns Result.success(InitializeCryptoResult.AuthenticationError)
|
||||
|
||||
val result = vaultRepository.unlockVaultAndSync(masterPassword = "")
|
||||
assertEquals(
|
||||
VaultUnlockResult.AuthenticationError,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with missing user state should return InvalidStateError `() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.unlockVaultAndSync(masterPassword = "")
|
||||
|
||||
assertEquals(
|
||||
VaultUnlockResult.InvalidStateError,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with missing user key should return InvalidStateError `() =
|
||||
runTest {
|
||||
val result = vaultRepository.unlockVaultAndSync(masterPassword = "")
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = null,
|
||||
)
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = "mockPrivateKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
assertEquals(
|
||||
VaultUnlockResult.InvalidStateError,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVaultAndSync with missing private key should return InvalidStateError `() =
|
||||
runTest {
|
||||
val result = vaultRepository.unlockVaultAndSync(masterPassword = "")
|
||||
fakeAuthDiskSource.storeUserKey(
|
||||
userId = "mockUserId",
|
||||
userKey = "mockKey-1",
|
||||
)
|
||||
fakeAuthDiskSource.storePrivateKey(
|
||||
userId = "mockUserId",
|
||||
privateKey = null,
|
||||
)
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
assertEquals(
|
||||
VaultUnlockResult.InvalidStateError,
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val MOCK_USER_STATE = UserStateJson(
|
||||
|
||||
Reference in New Issue
Block a user