mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 04:19:54 +03:00
Provide graceful fallbacks when there is no active user (#825)
This commit is contained in:
parent
8f21fb466e
commit
ab0cfdfdc2
2 changed files with 243 additions and 17 deletions
|
@ -351,7 +351,9 @@ class VaultRepositoryImpl(
|
|||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun getAuthCodesFlow(): StateFlow<DataState<List<VerificationCodeItem>>> {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return MutableStateFlow(
|
||||
DataState.Error(IllegalStateException("No active user"), null),
|
||||
)
|
||||
return vaultDataStateFlow
|
||||
.map { dataState ->
|
||||
dataState.map { vaultData ->
|
||||
|
@ -392,7 +394,9 @@ class VaultRepositoryImpl(
|
|||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun getAuthCodeFlow(cipherId: String): StateFlow<DataState<VerificationCodeItem?>> {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return MutableStateFlow(
|
||||
DataState.Error(IllegalStateException("No active user"), null),
|
||||
)
|
||||
return getVaultItemStateFlow(cipherId)
|
||||
.flatMapLatest { cipherDataState ->
|
||||
val cipher = cipherDataState.data
|
||||
|
@ -503,7 +507,7 @@ class VaultRepositoryImpl(
|
|||
)
|
||||
|
||||
override suspend fun createCipher(cipherView: CipherView): CreateCipherResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return CreateCipherResult.Error
|
||||
return vaultSdkSource
|
||||
.encryptCipher(
|
||||
userId = userId,
|
||||
|
@ -527,7 +531,7 @@ class VaultRepositoryImpl(
|
|||
}
|
||||
|
||||
override suspend fun hardDeleteCipher(cipherId: String): DeleteCipherResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return DeleteCipherResult.Error
|
||||
return ciphersService
|
||||
.hardDeleteCipher(cipherId)
|
||||
.onSuccess { vaultDiskSource.deleteCipher(userId, cipherId) }
|
||||
|
@ -541,7 +545,7 @@ class VaultRepositoryImpl(
|
|||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): DeleteCipherResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return DeleteCipherResult.Error
|
||||
return ciphersService
|
||||
.softDeleteCipher(cipherId)
|
||||
.fold(
|
||||
|
@ -570,7 +574,7 @@ class VaultRepositoryImpl(
|
|||
attachmentId: String,
|
||||
cipherView: CipherView,
|
||||
): DeleteAttachmentResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return DeleteAttachmentResult.Error
|
||||
return ciphersService
|
||||
.deleteCipherAttachment(
|
||||
cipherId = cipherId,
|
||||
|
@ -603,7 +607,7 @@ class VaultRepositoryImpl(
|
|||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): RestoreCipherResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return RestoreCipherResult.Error
|
||||
return ciphersService
|
||||
.restoreCipher(cipherId)
|
||||
.flatMap {
|
||||
|
@ -630,7 +634,7 @@ class VaultRepositoryImpl(
|
|||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): UpdateCipherResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return UpdateCipherResult.Error(null)
|
||||
return vaultSdkSource
|
||||
.encryptCipher(
|
||||
userId = userId,
|
||||
|
@ -664,7 +668,7 @@ class VaultRepositoryImpl(
|
|||
cipherView: CipherView,
|
||||
collectionIds: List<String>,
|
||||
): ShareCipherResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return ShareCipherResult.Error(null)
|
||||
return vaultSdkSource
|
||||
.encryptCipher(
|
||||
userId = userId,
|
||||
|
@ -695,7 +699,7 @@ class VaultRepositoryImpl(
|
|||
fileName: String,
|
||||
fileUri: Uri,
|
||||
): CreateAttachmentResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return CreateAttachmentResult.Error
|
||||
val attachmentView = AttachmentView(
|
||||
id = null,
|
||||
url = null,
|
||||
|
@ -757,7 +761,7 @@ class VaultRepositoryImpl(
|
|||
sendView: SendView,
|
||||
fileUri: Uri?,
|
||||
): CreateSendResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return CreateSendResult.Error
|
||||
return vaultSdkSource
|
||||
.encryptSend(
|
||||
userId = userId,
|
||||
|
@ -817,7 +821,7 @@ class VaultRepositoryImpl(
|
|||
sendId: String,
|
||||
sendView: SendView,
|
||||
): UpdateSendResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return UpdateSendResult.Error(null)
|
||||
return vaultSdkSource
|
||||
.encryptSend(
|
||||
userId = userId,
|
||||
|
@ -854,7 +858,7 @@ class VaultRepositoryImpl(
|
|||
}
|
||||
|
||||
override suspend fun removePasswordSend(sendId: String): RemovePasswordSendResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return RemovePasswordSendResult.Error(null)
|
||||
return sendsService
|
||||
.removeSendPassword(sendId = sendId)
|
||||
.fold(
|
||||
|
@ -882,7 +886,7 @@ class VaultRepositoryImpl(
|
|||
}
|
||||
|
||||
override suspend fun deleteSend(sendId: String): DeleteSendResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return DeleteSendResult.Error
|
||||
return sendsService
|
||||
.deleteSend(sendId)
|
||||
.onSuccess { vaultDiskSource.deleteSend(userId, sendId) }
|
||||
|
@ -896,7 +900,7 @@ class VaultRepositoryImpl(
|
|||
totpCode: String,
|
||||
time: DateTime,
|
||||
): GenerateTotpResult {
|
||||
val userId = requireNotNull(activeUserId)
|
||||
val userId = activeUserId ?: return GenerateTotpResult.Error
|
||||
return vaultSdkSource.generateTotp(
|
||||
time = time,
|
||||
userId = userId,
|
||||
|
|
|
@ -106,6 +106,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.net.UnknownHostException
|
||||
|
@ -1624,6 +1625,19 @@ class VaultRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createCipher with no active user should return CreateCipherResult failure`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.createCipher(cipherView = mockk())
|
||||
|
||||
assertEquals(
|
||||
CreateCipherResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createCipher with encryptCipher failure should return CreateCipherResult failure`() =
|
||||
runTest {
|
||||
|
@ -1701,6 +1715,22 @@ class VaultRepositoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateCipher with no active user should return UpdateCipherResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.updateCipher(
|
||||
cipherId = "cipherId",
|
||||
cipherView = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
UpdateCipherResult.Error(null),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateCipher with encryptCipher failure should return UpdateCipherResult failure`() =
|
||||
runTest {
|
||||
|
@ -1829,6 +1859,21 @@ class VaultRepositoryTest {
|
|||
assertEquals(UpdateCipherResult.Success, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hardDeleteCipher with no active user should return DeleteCipherResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.hardDeleteCipher(
|
||||
cipherId = "cipherId",
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DeleteCipherResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `hardDeleteCipher with ciphersService hardDeleteCipher failure should return DeleteCipherResult Error`() =
|
||||
|
@ -1859,6 +1904,22 @@ class VaultRepositoryTest {
|
|||
assertEquals(DeleteCipherResult.Success, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `softDeleteCipher with no active user should return DeleteCipherResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.softDeleteCipher(
|
||||
cipherId = "cipherId",
|
||||
cipherView = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DeleteCipherResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `softDeleteCipher with ciphersService softDeleteCipher failure should return DeleteCipherResult Error`() =
|
||||
|
@ -1919,6 +1980,23 @@ class VaultRepositoryTest {
|
|||
unmockkStatic(Cipher::toEncryptedNetworkCipherResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteCipherAttachment with no active user should return DeleteAttachmentResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.deleteCipherAttachment(
|
||||
cipherId = "cipherId",
|
||||
attachmentId = "attachmentId",
|
||||
cipherView = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DeleteAttachmentResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `deleteCipherAttachment with ciphersService deleteCipherAttachment failure should return DeleteAttachmentResult Error`() =
|
||||
|
@ -1990,6 +2068,22 @@ class VaultRepositoryTest {
|
|||
unmockkStatic(Cipher::toEncryptedNetworkCipherResponse)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `restoreCipher with no active user should return RestoreCipherResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.restoreCipher(
|
||||
cipherId = "cipherId",
|
||||
cipherView = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
RestoreCipherResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `restoreCipher with ciphersService restoreCipher failure should return RestoreCipherResult Error`() =
|
||||
|
@ -2048,6 +2142,22 @@ class VaultRepositoryTest {
|
|||
assertEquals(RestoreCipherResult.Success, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createSend with no active user should return CreateSendResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.createSend(
|
||||
sendView = mockk(),
|
||||
fileUri = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
CreateSendResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createSend with encryptSend failure should return CreateSendResult failure`() =
|
||||
runTest {
|
||||
|
@ -2237,6 +2347,22 @@ class VaultRepositoryTest {
|
|||
assertEquals(CreateSendResult.Success(mockSendViewResult), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateSend with no active user should return UpdateSendResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.updateSend(
|
||||
sendId = "sendId",
|
||||
sendView = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
UpdateSendResult.Error(null),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateSend with encryptSend failure should return UpdateSendResult failure`() =
|
||||
runTest {
|
||||
|
@ -2390,6 +2516,21 @@ class VaultRepositoryTest {
|
|||
assertEquals(UpdateSendResult.Success(mockSendViewResult), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `removePasswordSend with no active user should return RemovePasswordSendResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.removePasswordSend(
|
||||
sendId = "sendId",
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
RemovePasswordSendResult.Error(null),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `removePasswordSend with sendsService removeSendPassword Error should return RemovePasswordSendResult Error`() =
|
||||
|
@ -2448,6 +2589,21 @@ class VaultRepositoryTest {
|
|||
assertEquals(RemovePasswordSendResult.Success(mockSendView), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteSend with no active user should return DeleteSendResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.deleteSend(
|
||||
sendId = "sendId",
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
DeleteSendResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteSend with sendsService deleteSend failure should return DeleteSendResult Error`() =
|
||||
runTest {
|
||||
|
@ -2476,6 +2632,23 @@ class VaultRepositoryTest {
|
|||
assertEquals(DeleteSendResult.Success, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shareCipher with no active user should return ShareCipherResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.shareCipher(
|
||||
cipherId = "cipherId",
|
||||
cipherView = mockk(),
|
||||
collectionIds = emptyList(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
ShareCipherResult.Error(null),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `shareCipher with cipherService shareCipher success should return ShareCipherResultSuccess`() =
|
||||
|
@ -2581,6 +2754,25 @@ class VaultRepositoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createAttachment with no active user should return CreateAttachmentResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.createAttachment(
|
||||
cipherId = "cipherId",
|
||||
cipherView = mockk(),
|
||||
fileSizeBytes = "mockFileSize",
|
||||
fileName = "mockFileName",
|
||||
fileUri = mockk(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
CreateAttachmentResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `createAttachment with encryptCipher failure should return CreateAttachmentResult Error`() =
|
||||
|
@ -2907,6 +3099,22 @@ class VaultRepositoryTest {
|
|||
assertEquals(CreateAttachmentResult.Success(mockCipherView), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generateTotp with no active user should return GenerateTotpResult Error`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = vaultRepository.generateTotp(
|
||||
totpCode = "totpCode",
|
||||
time = DateTime.now(),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
GenerateTotpResult.Error,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generateTotp should return a success result on getting a code`() = runTest {
|
||||
val totpResponse = TotpResponse("Testcode", 30u)
|
||||
|
@ -2931,7 +3139,14 @@ class VaultRepositoryTest {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getVerificationCodeFlow for a single cipher should update data state when state changes`() =
|
||||
fun `getAuthCodeFlow with no active user should emit an error`() = runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
assertTrue(vaultRepository.getAuthCodeFlow(cipherId = "cipherId").value is DataState.Error)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getAuthCodeFlow for a single cipher should update data state when state changes`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val userId = "mockId-1"
|
||||
|
@ -2989,8 +3204,15 @@ class VaultRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getVerificationCodesFlow should update data state when state changes`() = runTest {
|
||||
fun `getAuthCodesFlow with no active user should emit an error`() = runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
assertTrue(vaultRepository.getAuthCodesFlow().value is DataState.Error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getAuthCodesFlow should update data state when state changes`() = runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val userId = "mockId-1"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue