mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
Add API for updating a cipher (#354)
This commit is contained in:
parent
266db5cc04
commit
bfc0e9831c
8 changed files with 168 additions and 2 deletions
|
@ -4,6 +4,8 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
* Defines raw calls under the /ciphers API with authentication applied.
|
||||
|
@ -15,4 +17,13 @@ interface CiphersApi {
|
|||
*/
|
||||
@POST("ciphers")
|
||||
suspend fun createCipher(@Body body: CipherJsonRequest): Result<SyncResponseJson.Cipher>
|
||||
|
||||
/**
|
||||
* Updates a cipher.
|
||||
*/
|
||||
@PUT("ciphers/{cipherId}")
|
||||
suspend fun updateCipher(
|
||||
@Path("cipherId") cipherId: String,
|
||||
@Body body: CipherJsonRequest,
|
||||
): Result<SyncResponseJson.Cipher>
|
||||
}
|
||||
|
|
|
@ -11,4 +11,12 @@ interface CiphersService {
|
|||
* Attempt to create a cipher.
|
||||
*/
|
||||
suspend fun createCipher(body: CipherJsonRequest): Result<SyncResponseJson.Cipher>
|
||||
|
||||
/**
|
||||
* Attempt to update a cipher.
|
||||
*/
|
||||
suspend fun updateCipher(
|
||||
cipherId: String,
|
||||
body: CipherJsonRequest,
|
||||
): Result<SyncResponseJson.Cipher>
|
||||
}
|
||||
|
|
|
@ -9,4 +9,13 @@ class CiphersServiceImpl constructor(
|
|||
) : CiphersService {
|
||||
override suspend fun createCipher(body: CipherJsonRequest): Result<SyncResponseJson.Cipher> =
|
||||
ciphersApi.createCipher(body = body)
|
||||
|
||||
override suspend fun updateCipher(
|
||||
cipherId: String,
|
||||
body: CipherJsonRequest,
|
||||
): Result<SyncResponseJson.Cipher> =
|
||||
ciphersApi.updateCipher(
|
||||
cipherId = cipherId,
|
||||
body = body,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.bitwarden.core.Kdf
|
|||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
|
@ -86,4 +87,12 @@ interface VaultRepository {
|
|||
* Attempt to create a cipher.
|
||||
*/
|
||||
suspend fun createCipher(cipherView: CipherView): CreateCipherResult
|
||||
|
||||
/**
|
||||
* Attempt to update a cipher.
|
||||
*/
|
||||
suspend fun updateCipher(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): UpdateCipherResult
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
|
@ -256,6 +257,26 @@ class VaultRepositoryImpl constructor(
|
|||
},
|
||||
)
|
||||
|
||||
override suspend fun updateCipher(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): UpdateCipherResult =
|
||||
vaultSdkSource
|
||||
.encryptCipher(cipherView = cipherView)
|
||||
.flatMap { cipher ->
|
||||
ciphersService.updateCipher(
|
||||
cipherId = cipherId,
|
||||
body = cipher.toEncryptedNetworkCipher(),
|
||||
)
|
||||
}
|
||||
.fold(
|
||||
onFailure = { UpdateCipherResult.Error },
|
||||
onSuccess = {
|
||||
sync()
|
||||
UpdateCipherResult.Success
|
||||
},
|
||||
)
|
||||
|
||||
// TODO: This is temporary. Eventually this needs to be based on the presence of various
|
||||
// user keys but this will likely require SDK updates to support this (BIT-1190).
|
||||
private fun setVaultToUnlocked(userId: String) {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
/**
|
||||
* Models result of updating a cipher.
|
||||
*/
|
||||
sealed class UpdateCipherResult {
|
||||
|
||||
/**
|
||||
* Cipher updated successfully.
|
||||
*/
|
||||
data object Success : UpdateCipherResult()
|
||||
|
||||
/**
|
||||
* Generic error while updating cipher.
|
||||
*/
|
||||
data object Error : UpdateCipherResult()
|
||||
}
|
|
@ -19,7 +19,7 @@ class CiphersServiceTest : BaseServiceTest() {
|
|||
|
||||
@Test
|
||||
fun `createCipher should return the correct response`() = runTest {
|
||||
server.enqueue(MockResponse().setBody(CREATE_CIPHER_SUCCESS_JSON))
|
||||
server.enqueue(MockResponse().setBody(CREATE_UPDATE_CIPHER_SUCCESS_JSON))
|
||||
val result = ciphersService.createCipher(
|
||||
body = createMockCipherJsonRequest(number = 1),
|
||||
)
|
||||
|
@ -28,9 +28,22 @@ class CiphersServiceTest : BaseServiceTest() {
|
|||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateCipher should return the correct response`() = runTest {
|
||||
server.enqueue(MockResponse().setBody(CREATE_UPDATE_CIPHER_SUCCESS_JSON))
|
||||
val result = ciphersService.updateCipher(
|
||||
cipherId = "cipher-id-1",
|
||||
body = createMockCipherJsonRequest(number = 1),
|
||||
)
|
||||
assertEquals(
|
||||
createMockCipher(number = 1),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val CREATE_CIPHER_SUCCESS_JSON = """
|
||||
private const val CREATE_UPDATE_CIPHER_SUCCESS_JSON = """
|
||||
{
|
||||
"notes": "mockNotes-1",
|
||||
"attachments": [
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSend
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
|
@ -1484,6 +1485,83 @@ class VaultRepositoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateCipher with encryptCipher failure should return UpdateCipherResult failure`() =
|
||||
runTest {
|
||||
val cipherId = "cipherId1234"
|
||||
val mockCipherView = createMockCipherView(number = 1)
|
||||
coEvery {
|
||||
vaultSdkSource.encryptCipher(cipherView = mockCipherView)
|
||||
} returns IllegalStateException().asFailure()
|
||||
|
||||
val result = vaultRepository.updateCipher(
|
||||
cipherId = cipherId,
|
||||
cipherView = mockCipherView,
|
||||
)
|
||||
|
||||
assertEquals(UpdateCipherResult.Error, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `updateCipher with ciphersService updateCipher failure should return UpdateCipherResult failure`() =
|
||||
runTest {
|
||||
val cipherId = "cipherId1234"
|
||||
val mockCipherView = createMockCipherView(number = 1)
|
||||
coEvery {
|
||||
vaultSdkSource.encryptCipher(cipherView = mockCipherView)
|
||||
} returns createMockSdkCipher(number = 1).asSuccess()
|
||||
coEvery {
|
||||
ciphersService.updateCipher(
|
||||
cipherId = cipherId,
|
||||
body = createMockCipherJsonRequest(number = 1),
|
||||
)
|
||||
} returns IllegalStateException().asFailure()
|
||||
|
||||
val result = vaultRepository.updateCipher(
|
||||
cipherId = cipherId,
|
||||
cipherView = mockCipherView,
|
||||
)
|
||||
|
||||
assertEquals(UpdateCipherResult.Error, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MaxLineLength")
|
||||
fun `updateCipher with ciphersService updateCipher success should return UpdateCipherResult success`() =
|
||||
runTest {
|
||||
val cipherId = "cipherId1234"
|
||||
val mockCipherView = createMockCipherView(number = 1)
|
||||
coEvery {
|
||||
vaultSdkSource.encryptCipher(cipherView = mockCipherView)
|
||||
} returns createMockSdkCipher(number = 1).asSuccess()
|
||||
coEvery {
|
||||
ciphersService.updateCipher(
|
||||
cipherId = cipherId,
|
||||
body = createMockCipherJsonRequest(number = 1),
|
||||
)
|
||||
} returns createMockCipher(number = 1).asSuccess()
|
||||
coEvery {
|
||||
syncService.sync()
|
||||
} returns Result.success(createMockSyncResponse(1))
|
||||
coEvery {
|
||||
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
|
||||
} returns listOf(createMockCipherView(1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
|
||||
} returns listOf(createMockFolderView(1)).asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(1)))
|
||||
} returns listOf(createMockSendView(1)).asSuccess()
|
||||
|
||||
val result = vaultRepository.updateCipher(
|
||||
cipherId = cipherId,
|
||||
cipherView = mockCipherView,
|
||||
)
|
||||
|
||||
assertEquals(UpdateCipherResult.Success, result)
|
||||
}
|
||||
|
||||
//region Helper functions
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue