Skip to content

Commit 6902c19

Browse files
authored
🍒 [PM-32802] fix: 400 error when archiving/unarchiving org-owned ciphers (#6596)
1 parent 07d0600 commit 6902c19

8 files changed

Lines changed: 256 additions & 231 deletions

File tree

app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerImpl.kt

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import com.bitwarden.core.data.util.asSuccess
88
import com.bitwarden.core.data.util.flatMap
99
import com.bitwarden.data.manager.file.FileManager
1010
import com.bitwarden.data.manager.model.DownloadResult
11+
import com.bitwarden.network.model.ArchiveCipherResponseJson
1112
import com.bitwarden.network.model.AttachmentJsonResponse
1213
import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest
1314
import com.bitwarden.network.model.CreateCipherResponseJson
1415
import com.bitwarden.network.model.ShareCipherJsonRequest
16+
import com.bitwarden.network.model.UnarchiveCipherResponseJson
1517
import com.bitwarden.network.model.UpdateCipherCollectionsJsonRequest
1618
import com.bitwarden.network.model.UpdateCipherResponseJson
1719
import com.bitwarden.network.service.CiphersService
@@ -168,33 +170,29 @@ class CipherManagerImpl(
168170
cipherView: CipherView,
169171
): ArchiveCipherResult {
170172
val userId = activeUserId ?: return ArchiveCipherResult.Error(NoActiveUserException())
171-
return cipherView
172-
.encryptCipherAndCheckForMigration(userId = userId, cipherId = cipherId)
173-
.flatMap { encryptionContext ->
174-
ciphersService
175-
.archiveCipher(cipherId = cipherId)
176-
.flatMap {
177-
vaultSdkSource.decryptCipher(
173+
return ciphersService
174+
.archiveCipher(cipherId = cipherId)
175+
.flatMap { response ->
176+
when (response) {
177+
is ArchiveCipherResponseJson.Invalid -> {
178+
IllegalStateException(response.firstValidationErrorMessage)
179+
.asFailure()
180+
}
181+
182+
is ArchiveCipherResponseJson.Success -> {
183+
vaultDiskSource.saveCipher(
178184
userId = userId,
179-
cipher = encryptionContext.cipher,
185+
cipher = response.cipher.copy(
186+
collectionIds = cipherView.collectionIds,
187+
),
188+
)
189+
settingsDiskSource.storeIntroducingArchiveActionCardDismissed(
190+
userId = userId,
191+
isDismissed = true,
180192
)
193+
response.asSuccess()
181194
}
182-
}
183-
.flatMap {
184-
vaultSdkSource.encryptCipher(
185-
userId = userId,
186-
cipherView = it.copy(archivedDate = clock.instant()),
187-
)
188-
}
189-
.onSuccess {
190-
vaultDiskSource.saveCipher(
191-
userId = userId,
192-
cipher = it.toEncryptedNetworkCipherResponse(),
193-
)
194-
settingsDiskSource.storeIntroducingArchiveActionCardDismissed(
195-
userId = userId,
196-
isDismissed = true,
197-
)
195+
}
198196
}
199197
.fold(
200198
onSuccess = { ArchiveCipherResult.Success },
@@ -207,29 +205,25 @@ class CipherManagerImpl(
207205
cipherView: CipherView,
208206
): UnarchiveCipherResult {
209207
val userId = activeUserId ?: return UnarchiveCipherResult.Error(NoActiveUserException())
210-
return cipherView
211-
.encryptCipherAndCheckForMigration(userId = userId, cipherId = cipherId)
212-
.flatMap { encryptionContext ->
213-
ciphersService
214-
.unarchiveCipher(cipherId = cipherId)
215-
.flatMap {
216-
vaultSdkSource.decryptCipher(
208+
return ciphersService
209+
.unarchiveCipher(cipherId = cipherId)
210+
.flatMap { response ->
211+
when (response) {
212+
is UnarchiveCipherResponseJson.Invalid -> {
213+
IllegalStateException(response.firstValidationErrorMessage)
214+
.asFailure()
215+
}
216+
217+
is UnarchiveCipherResponseJson.Success -> {
218+
vaultDiskSource.saveCipher(
217219
userId = userId,
218-
cipher = encryptionContext.cipher,
220+
cipher = response.cipher.copy(
221+
collectionIds = cipherView.collectionIds,
222+
),
219223
)
224+
response.asSuccess()
220225
}
221-
}
222-
.flatMap {
223-
vaultSdkSource.encryptCipher(
224-
userId = userId,
225-
cipherView = it.copy(archivedDate = null),
226-
)
227-
}
228-
.onSuccess {
229-
vaultDiskSource.saveCipher(
230-
userId = userId,
231-
cipher = it.toEncryptedNetworkCipherResponse(),
232-
)
226+
}
233227
}
234228
.fold(
235229
onSuccess = { UnarchiveCipherResult.Success },
@@ -255,6 +249,9 @@ class CipherManagerImpl(
255249
): DeleteCipherResult {
256250
val userId = activeUserId
257251
?: return DeleteCipherResult.Error(error = NoActiveUserException())
252+
// Unlike archive/unarchive, soft delete requires edit permissions, so the
253+
// migration check is intentional here to ensure the cipher is up-to-date
254+
// before deletion.
258255
return cipherView
259256
.encryptCipherAndCheckForMigration(userId = userId, cipherId = cipherId)
260257
.flatMap { encryptionContext ->

0 commit comments

Comments
 (0)