Skip to content

Commit 18217a6

Browse files
[PM-18211] Add alert for cipher decryption failure when vault loads (#1769)
1 parent a924eb1 commit 18217a6

31 files changed

+556
-261
lines changed

BitwardenShared/Core/Vault/Helpers/TestHelpers/MockVaultListPreparedDataBuilder+Extensions.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ extension MockVaultListPreparedDataBuilder {
88
func setUpCallOrderHelper() -> MockCallOrderHelper {
99
let helper = MockCallOrderHelper()
1010

11+
addCipherDecryptionFailureCipherClosure = { _ -> VaultListPreparedDataBuilder in
12+
helper.recordCall("addCipherDecryptionFailure")
13+
return self
14+
}
1115
addFavoriteItemCipherClosure = { _ -> VaultListPreparedDataBuilder in
1216
helper.recordCall("addFavoriteItem")
1317
return self
@@ -57,6 +61,7 @@ extension MockVaultListPreparedDataBuilder {
5761
}
5862

5963
func setUpFluentReturn() {
64+
addCipherDecryptionFailureCipherReturnValue = self
6065
addFavoriteItemCipherReturnValue = self
6166
addFolderItemCipherFilterFoldersReturnValue = self
6267
addItemForGroupWithReturnValue = self

BitwardenShared/Core/Vault/Helpers/TestHelpers/MockVaultListSectionsBuilder+Extensions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ extension MockVaultListSectionsBuilder {
44
func setUpCallOrderHelper() -> MockCallOrderHelper {
55
let helper = MockCallOrderHelper()
66

7+
addCipherDecryptionFailureIdsClosure = { () -> VaultListSectionsBuilder in
8+
helper.recordCall("addCipherDecryptionFailureIds")
9+
return self
10+
}
711
addTOTPSectionClosure = { () -> VaultListSectionsBuilder in
812
helper.recordCall("addTOTPSection")
913
return self

BitwardenShared/Core/Vault/Helpers/VaultListDataPreparator.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ struct DefaultVaultListDataPreparator: VaultListDataPreparator {
9393
}
9494

9595
preparedDataBuilder = preparedDataBuilder
96+
.addCipherDecryptionFailure(cipher: decryptedCipher)
9697
.addFolderItem(cipher: decryptedCipher, filter: filter, folders: folders)
9798
.addFavoriteItem(cipher: decryptedCipher)
9899
.addNoFolderItem(cipher: decryptedCipher)

BitwardenShared/Core/Vault/Helpers/VaultListDataPreparatorTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class VaultListDataPreparatorTests: BitwardenTestCase { // swiftlint:disable:thi
9393
"prepareFolders",
9494
"prepareCollections",
9595
"incrementTOTPCount",
96+
"addCipherDecryptionFailure",
9697
"addFolderItem",
9798
"addFavoriteItem",
9899
"addNoFolderItem",
@@ -117,6 +118,7 @@ class VaultListDataPreparatorTests: BitwardenTestCase { // swiftlint:disable:thi
117118
XCTAssertEqual(mockCallOrderHelper.callOrder, [
118119
"prepareFolders",
119120
"prepareCollections",
121+
"addCipherDecryptionFailure",
120122
"addFolderItem",
121123
"addFavoriteItem",
122124
"addNoFolderItem",
@@ -198,6 +200,7 @@ class VaultListDataPreparatorTests: BitwardenTestCase { // swiftlint:disable:thi
198200
"prepareFolders",
199201
"prepareCollections",
200202
"incrementTOTPCount",
203+
"addCipherDecryptionFailure",
201204
"addFolderItem",
202205
"addFavoriteItem",
203206
"addNoFolderItem",
@@ -231,6 +234,7 @@ class VaultListDataPreparatorTests: BitwardenTestCase { // swiftlint:disable:thi
231234
"prepareFolders",
232235
"prepareCollections",
233236
"incrementTOTPCount",
237+
"addCipherDecryptionFailure",
234238
"addFolderItem",
235239
"addFavoriteItem",
236240
"addNoFolderItem",

BitwardenShared/Core/Vault/Helpers/VaultListDirectors/MainVaultListDirectorStrategy.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct MainVaultListDirectorStrategy: VaultListDirectorStrategy {
2222

2323
func build(
2424
filter: VaultListFilter
25-
) async throws -> AsyncThrowingPublisher<AnyPublisher<[VaultListSection], Error>> {
25+
) async throws -> AsyncThrowingPublisher<AnyPublisher<VaultListData, Error>> {
2626
try await Publishers.CombineLatest3(
2727
cipherService.ciphersPublisher(),
2828
collectionService.collectionsPublisher(),
@@ -43,22 +43,22 @@ struct MainVaultListDirectorStrategy: VaultListDirectorStrategy {
4343
/// - collections: Collections to filter and include in the sections.
4444
/// - folders: Folders to filter and include in the sections.
4545
/// - filter: Filter to be used to build the sections.
46-
/// - Returns: Sections to be displayed to the user.
46+
/// - Returns: Vault list data containing the sections to be displayed to the user.
4747
func build(
4848
from ciphers: [Cipher],
4949
collections: [Collection],
5050
folders: [Folder],
5151
filter: VaultListFilter
52-
) async throws -> [VaultListSection] {
53-
guard !ciphers.isEmpty else { return [] }
52+
) async throws -> VaultListData {
53+
guard !ciphers.isEmpty else { return VaultListData() }
5454

5555
guard let preparedData = try await vaultListDataPreparator.prepareData(
5656
from: ciphers,
5757
collections: collections,
5858
folders: folders,
5959
filter: filter
6060
) else {
61-
return []
61+
return VaultListData()
6262
}
6363

6464
var builder = builderFactory.make(withData: preparedData)
@@ -72,6 +72,7 @@ struct MainVaultListDirectorStrategy: VaultListDirectorStrategy {
7272
.addTypesSection()
7373
.addFoldersSection()
7474
.addCollectionsSection()
75+
.addCipherDecryptionFailureIds()
7576

7677
if filter.addTrashGroup {
7778
builder = builder.addTrashSection()

BitwardenShared/Core/Vault/Helpers/VaultListDirectors/MainVaultListDirectorStrategyTests.swift

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
6363
folderService.foldersSubject.value = []
6464

6565
var iteratorPublisher = try await subject.build(filter: VaultListFilter()).makeAsyncIterator()
66-
let sections = try await iteratorPublisher.next()
66+
let vaultListData = try await iteratorPublisher.next()
6767

68-
XCTAssertEqual(sections, [])
68+
XCTAssertEqual(vaultListData, VaultListData())
6969
}
7070

7171
/// `build(filter:)` returns empty when preparing data fails to return data.
@@ -77,9 +77,9 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
7777
vaultListDataPreparator.prepareDataFromCollectionsFoldersFilterReturnValue = nil
7878

7979
var iteratorPublisher = try await subject.build(filter: VaultListFilter()).makeAsyncIterator()
80-
let sections = try await iteratorPublisher.next()
80+
let vaultListData = try await iteratorPublisher.next()
8181

82-
XCTAssertEqual(sections, [])
82+
XCTAssertEqual(vaultListData, VaultListData())
8383
}
8484

8585
/// `build(filter:)` returns the sections built adding TOTP group and trash group as indicated by the filter.
@@ -90,11 +90,13 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
9090

9191
vaultListDataPreparator.prepareDataFromCollectionsFoldersFilterReturnValue = VaultListPreparedData()
9292

93-
vaultListSectionsBuilder.buildReturnValue = [
94-
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
95-
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
96-
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
97-
]
93+
vaultListSectionsBuilder.buildReturnValue = VaultListData(
94+
sections: [
95+
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
96+
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
97+
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
98+
]
99+
)
98100

99101
var iteratorPublisher = try await subject.build(
100102
filter: VaultListFilter(
@@ -103,15 +105,16 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
103105
)
104106
).makeAsyncIterator()
105107
let result = try await iteratorPublisher.next()
106-
let sections = try XCTUnwrap(result)
108+
let vaultListData = try XCTUnwrap(result)
107109

108-
XCTAssertEqual(sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
110+
XCTAssertEqual(vaultListData.sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
109111
XCTAssertEqual(mockCallOrderHelper.callOrder, [
110112
"addTOTPSection",
111113
"addFavoritesSection",
112114
"addTypesSection",
113115
"addFoldersSection",
114116
"addCollectionsSection",
117+
"addCipherDecryptionFailureIds",
115118
"addTrashSection",
116119
])
117120
}
@@ -124,11 +127,13 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
124127

125128
vaultListDataPreparator.prepareDataFromCollectionsFoldersFilterReturnValue = VaultListPreparedData()
126129

127-
vaultListSectionsBuilder.buildReturnValue = [
128-
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
129-
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
130-
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
131-
]
130+
vaultListSectionsBuilder.buildReturnValue = VaultListData(
131+
sections: [
132+
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
133+
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
134+
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
135+
]
136+
)
132137

133138
var iteratorPublisher = try await subject.build(
134139
filter: VaultListFilter(
@@ -137,15 +142,16 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
137142
)
138143
).makeAsyncIterator()
139144
let result = try await iteratorPublisher.next()
140-
let sections = try XCTUnwrap(result)
145+
let vaultListData = try XCTUnwrap(result)
141146

142-
XCTAssertEqual(sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
147+
XCTAssertEqual(vaultListData.sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
143148
XCTAssertEqual(mockCallOrderHelper.callOrder, [
144149
"addTOTPSection",
145150
"addFavoritesSection",
146151
"addTypesSection",
147152
"addFoldersSection",
148153
"addCollectionsSection",
154+
"addCipherDecryptionFailureIds",
149155
])
150156
}
151157

@@ -158,11 +164,13 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
158164

159165
vaultListDataPreparator.prepareDataFromCollectionsFoldersFilterReturnValue = VaultListPreparedData()
160166

161-
vaultListSectionsBuilder.buildReturnValue = [
162-
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
163-
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
164-
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
165-
]
167+
vaultListSectionsBuilder.buildReturnValue = VaultListData(
168+
sections: [
169+
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
170+
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
171+
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
172+
]
173+
)
166174

167175
var iteratorPublisher = try await subject.build(
168176
filter: VaultListFilter(
@@ -171,14 +179,15 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
171179
)
172180
).makeAsyncIterator()
173181
let result = try await iteratorPublisher.next()
174-
let sections = try XCTUnwrap(result)
182+
let vaultListData = try XCTUnwrap(result)
175183

176-
XCTAssertEqual(sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
184+
XCTAssertEqual(vaultListData.sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
177185
XCTAssertEqual(mockCallOrderHelper.callOrder, [
178186
"addFavoritesSection",
179187
"addTypesSection",
180188
"addFoldersSection",
181189
"addCollectionsSection",
190+
"addCipherDecryptionFailureIds",
182191
"addTrashSection",
183192
])
184193
}
@@ -191,11 +200,13 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
191200

192201
vaultListDataPreparator.prepareDataFromCollectionsFoldersFilterReturnValue = VaultListPreparedData()
193202

194-
vaultListSectionsBuilder.buildReturnValue = [
195-
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
196-
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
197-
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
198-
]
203+
vaultListSectionsBuilder.buildReturnValue = VaultListData(
204+
sections: [
205+
VaultListSection(id: "TestID1", items: [.fixture()], name: "Test1"),
206+
VaultListSection(id: "TestID2", items: [.fixture()], name: "Test2"),
207+
VaultListSection(id: "TestID3", items: [.fixture()], name: "Test3"),
208+
]
209+
)
199210

200211
var iteratorPublisher = try await subject.build(
201212
filter: VaultListFilter(
@@ -204,14 +215,15 @@ class MainVaultListDirectorStrategyTests: BitwardenTestCase {
204215
)
205216
).makeAsyncIterator()
206217
let result = try await iteratorPublisher.next()
207-
let sections = try XCTUnwrap(result)
218+
let vaultListData = try XCTUnwrap(result)
208219

209-
XCTAssertEqual(sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
220+
XCTAssertEqual(vaultListData.sections.map(\.id), ["TestID1", "TestID2", "TestID3"])
210221
XCTAssertEqual(mockCallOrderHelper.callOrder, [
211222
"addFavoritesSection",
212223
"addTypesSection",
213224
"addFoldersSection",
214225
"addCollectionsSection",
226+
"addCipherDecryptionFailureIds",
215227
])
216228
}
217229
}

BitwardenShared/Core/Vault/Helpers/VaultListDirectors/MainVaultListGroupDirectorStrategy.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct MainVaultListGroupDirectorStrategy: VaultListDirectorStrategy {
2222

2323
func build(
2424
filter: VaultListFilter
25-
) async throws -> AsyncThrowingPublisher<AnyPublisher<[VaultListSection], Error>> {
25+
) async throws -> AsyncThrowingPublisher<AnyPublisher<VaultListData, Error>> {
2626
try await Publishers.CombineLatest3(
2727
cipherService.ciphersPublisher(),
2828
collectionService.collectionsPublisher(),
@@ -43,22 +43,22 @@ struct MainVaultListGroupDirectorStrategy: VaultListDirectorStrategy {
4343
/// - collections: Collections to filter and include in the sections.
4444
/// - folders: Folders to filter and include in the sections.
4545
/// - filter: Filter to be used to build the sections.
46-
/// - Returns: Sections to be displayed to the user.
46+
/// - Returns: Vault list data containing the sections to be displayed to the user.
4747
func build(
4848
from ciphers: [Cipher],
4949
collections: [Collection],
5050
folders: [Folder],
5151
filter: VaultListFilter
52-
) async throws -> [VaultListSection] {
53-
guard !ciphers.isEmpty else { return [] }
52+
) async throws -> VaultListData {
53+
guard !ciphers.isEmpty else { return VaultListData() }
5454

5555
guard let preparedGroupData = try await vaultListDataPreparator.prepareGroupData(
5656
from: ciphers,
5757
collections: collections,
5858
folders: folders,
5959
filter: filter
6060
) else {
61-
return []
61+
return VaultListData()
6262
}
6363

6464
var builder = builderFactory.make(withData: preparedGroupData)

0 commit comments

Comments
 (0)