From 6dd141091eeea35bf3b79454f62fd7fd2426310b Mon Sep 17 00:00:00 2001 From: Federico Maccaroni Date: Wed, 30 Oct 2024 17:36:52 -0300 Subject: [PATCH 1/2] PM-10401 Implemented SSH Key type handling so it can be shown in the vault if the SSH key feature flag is enabled. --- .../Platform/Models/Enum/FeatureFlag.swift | 3 + .../Models/Enum/FeatureFlagTests.swift | 19 ++ .../Vault/Extensions/BitwardenSdk+Vault.swift | 3 +- .../Extensions/BitwardenSdkVaultTests.swift | 15 ++ .../Core/Vault/Models/Enum/CipherType.swift | 12 ++ .../Vault/Models/Enum/CipherTypeTests.swift | 11 + .../Models/Request/CipherRequestModel.swift | 4 + .../Vault/Repositories/VaultRepository.swift | 23 +- .../Repositories/VaultRepositoryTests.swift | 196 ++++++++++++++++++ .../API/Fixtures/syncWithCiphers.json | 18 ++ .../Fixtures/syncWithCiphersCollections.json | 18 ++ .../en.lproj/Localizable.strings | 1 + .../Vault/VaultGroup/VaultGroupState.swift | 9 +- .../VaultGroup/VaultGroupViewTests.swift | 7 + .../test_snapshot_emptySSHKey.1.png | Bin 0 -> 74327 bytes .../Vault/VaultList/VaultListGroup.swift | 7 + .../Vault/VaultList/VaultListGroupTests.swift | 6 + .../Vault/Vault/VaultList/VaultListItem.swift | 2 + .../Vault/VaultList/VaultListItemTests.swift | 4 + .../Vault/Vault/VaultList/VaultListView.swift | 4 + .../AddEditItem/AddEditItemProcessor.swift | 4 +- .../AddEditItemProcessorTests.swift | 37 ++++ .../Extensions/CipherViewUpdateTests.swift | 16 ++ 23 files changed, 411 insertions(+), 8 deletions(-) create mode 100644 BitwardenShared/UI/Vault/Vault/VaultGroup/__Snapshots__/VaultGroupViewTests/test_snapshot_emptySSHKey.1.png diff --git a/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift b/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift index f6c6945f7..5980d0aff 100644 --- a/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift +++ b/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift @@ -24,6 +24,8 @@ enum FeatureFlag: String, CaseIterable, Codable { /// A feature flag for the create account flow. case nativeCreateAccountFlow = "native-create-account-flow" + case sshKeyVaultItem = "ssh-key-vault-item" + // MARK: Test Flags /// A test feature flag that isn't remotely configured and has no initial value. @@ -81,6 +83,7 @@ enum FeatureFlag: String, CaseIterable, Codable { .importLoginsFlow, .nativeCarouselFlow, .nativeCreateAccountFlow, + .sshKeyVaultItem, .testLocalFeatureFlag, .testLocalInitialBoolFlag, .testLocalInitialIntFlag, diff --git a/BitwardenShared/Core/Platform/Models/Enum/FeatureFlagTests.swift b/BitwardenShared/Core/Platform/Models/Enum/FeatureFlagTests.swift index aa16239ef..75a8489f5 100644 --- a/BitwardenShared/Core/Platform/Models/Enum/FeatureFlagTests.swift +++ b/BitwardenShared/Core/Platform/Models/Enum/FeatureFlagTests.swift @@ -12,6 +12,25 @@ final class FeatureFlagTests: BitwardenTestCase { XCTAssertEqual(filtered, []) } + /// `getter:isRemotelyConfigured` returns the correct value for each flag. + func test_isRemotelyConfigured() { + XCTAssertTrue(FeatureFlag.emailVerification.isRemotelyConfigured) + XCTAssertTrue(FeatureFlag.testRemoteInitialBoolFlag.isRemotelyConfigured) + XCTAssertTrue(FeatureFlag.testRemoteInitialIntFlag.isRemotelyConfigured) + XCTAssertTrue(FeatureFlag.testRemoteInitialStringFlag.isRemotelyConfigured) + + XCTAssertFalse(FeatureFlag.enableAuthenticatorSync.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.enableCipherKeyEncryption.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.importLoginsFlow.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.nativeCarouselFlow.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.nativeCreateAccountFlow.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.sshKeyVaultItem.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.testLocalFeatureFlag.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.testLocalInitialBoolFlag.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.testLocalInitialIntFlag.isRemotelyConfigured) + XCTAssertFalse(FeatureFlag.testLocalInitialStringFlag.isRemotelyConfigured) + } + /// `name` formats the raw value of a feature flag func test_name() { XCTAssertEqual(FeatureFlag.testLocalFeatureFlag.name, "Test Local Feature Flag") diff --git a/BitwardenShared/Core/Vault/Extensions/BitwardenSdk+Vault.swift b/BitwardenShared/Core/Vault/Extensions/BitwardenSdk+Vault.swift index b956d0e84..647862425 100644 --- a/BitwardenShared/Core/Vault/Extensions/BitwardenSdk+Vault.swift +++ b/BitwardenShared/Core/Vault/Extensions/BitwardenSdk+Vault.swift @@ -390,8 +390,7 @@ extension BitwardenSdk.CipherType { case .identity: self = .identity case .sshKey: - // TODO: PM-10401 set self = .sshKey when SDK is ready. - self = .init(rawValue: 5)! + self = .sshKey } } } diff --git a/BitwardenShared/Core/Vault/Extensions/BitwardenSdkVaultTests.swift b/BitwardenShared/Core/Vault/Extensions/BitwardenSdkVaultTests.swift index b4fcfdc09..e98e39cd0 100644 --- a/BitwardenShared/Core/Vault/Extensions/BitwardenSdkVaultTests.swift +++ b/BitwardenShared/Core/Vault/Extensions/BitwardenSdkVaultTests.swift @@ -5,6 +5,21 @@ import XCTest @testable import BitwardenShared +// MARK: - BitwardenSdk.CipherType + +class BitwardenSdkVaultBitwardenCipherTypeTests: BitwardenTestCase { // swiftlint:disable:this type_name + // MARK: Tests + + /// `init(type:)` initializes the SDK cipher type based on the cipher type. + func test_init_byCipherType() { + XCTAssertEqual(BitwardenSdk.CipherType(.login), .login) + XCTAssertEqual(BitwardenSdk.CipherType(.card), .card) + XCTAssertEqual(BitwardenSdk.CipherType(.identity), .identity) + XCTAssertEqual(BitwardenSdk.CipherType(.secureNote), .secureNote) + XCTAssertEqual(BitwardenSdk.CipherType(.sshKey), .sshKey) + } +} + // MARK: - Cipher class BitwardenSdkVaultCipherTests: BitwardenTestCase { diff --git a/BitwardenShared/Core/Vault/Models/Enum/CipherType.swift b/BitwardenShared/Core/Vault/Models/Enum/CipherType.swift index aaac24115..86edcfa00 100644 --- a/BitwardenShared/Core/Vault/Models/Enum/CipherType.swift +++ b/BitwardenShared/Core/Vault/Models/Enum/CipherType.swift @@ -32,6 +32,8 @@ extension CipherType { self = .login case .secureNote: self = .secureNote + case .sshKey: + self = .sshKey case .collection, .folder, .noFolder, @@ -61,4 +63,14 @@ extension CipherType: Menuable { extension CipherType { /// These are the cases of `CipherType` that the user can use to create a cipher. static let canCreateCases: [CipherType] = [.login, .card, .identity, .secureNote] + + /// The allowed custom field types per cipher type. + var allowedFieldTypes: [FieldType] { + switch self { + case .card, .identity, .login: + return [.text, .hidden, .boolean, .linked] + case .secureNote, .sshKey: + return [.text, .hidden, .boolean] + } + } } diff --git a/BitwardenShared/Core/Vault/Models/Enum/CipherTypeTests.swift b/BitwardenShared/Core/Vault/Models/Enum/CipherTypeTests.swift index e5f160171..2a30c52d9 100644 --- a/BitwardenShared/Core/Vault/Models/Enum/CipherTypeTests.swift +++ b/BitwardenShared/Core/Vault/Models/Enum/CipherTypeTests.swift @@ -5,12 +5,22 @@ import XCTest class CipherTypeTests: BitwardenTestCase { // MARK: Tests + /// `getter:allowedFieldTypes` return the correct `FielldType` array for the given cipher type.. + func test_allowedFieldTypes() { + XCTAssertEqual(CipherType.login.allowedFieldTypes, [.text, .hidden, .boolean, .linked]) + XCTAssertEqual(CipherType.card.allowedFieldTypes, [.text, .hidden, .boolean, .linked]) + XCTAssertEqual(CipherType.identity.allowedFieldTypes, [.text, .hidden, .boolean, .linked]) + XCTAssertEqual(CipherType.secureNote.allowedFieldTypes, [.text, .hidden, .boolean]) + XCTAssertEqual(CipherType.sshKey.allowedFieldTypes, [.text, .hidden, .boolean]) + } + /// `localizedName` returns the correct values. func test_localizedName() { XCTAssertEqual(CipherType.card.localizedName, Localizations.typeCard) XCTAssertEqual(CipherType.identity.localizedName, Localizations.typeIdentity) XCTAssertEqual(CipherType.login.localizedName, Localizations.typeLogin) XCTAssertEqual(CipherType.secureNote.localizedName, Localizations.typeSecureNote) + XCTAssertEqual(CipherType.sshKey.localizedName, Localizations.sshKey) } /// `init` with a `VaultListGroup` produces the correct value. @@ -21,6 +31,7 @@ class CipherTypeTests: BitwardenTestCase { XCTAssertEqual(CipherType(group: .identity), .identity) XCTAssertEqual(CipherType(group: .login), .login) XCTAssertEqual(CipherType(group: .secureNote), .secureNote) + XCTAssertEqual(CipherType(group: .sshKey), .sshKey) XCTAssertNil(CipherType(group: .trash)) } diff --git a/BitwardenShared/Core/Vault/Models/Request/CipherRequestModel.swift b/BitwardenShared/Core/Vault/Models/Request/CipherRequestModel.swift index 132bedba4..94584d87d 100644 --- a/BitwardenShared/Core/Vault/Models/Request/CipherRequestModel.swift +++ b/BitwardenShared/Core/Vault/Models/Request/CipherRequestModel.swift @@ -56,6 +56,9 @@ struct CipherRequestModel: JSONRequestBody { /// Secure note data if the cipher is a secure note. let secureNote: CipherSecureNoteModel? + /// SSH key data if the cipher is an SSH Key. + let sshKey: CipherSSHKeyModel? + /// The type of the cipher. let type: CipherType } @@ -85,6 +88,7 @@ extension CipherRequestModel { passwordHistory: cipher.passwordHistory?.map(CipherPasswordHistoryModel.init), reprompt: CipherRepromptType(type: cipher.reprompt), secureNote: cipher.secureNote.map(CipherSecureNoteModel.init), + sshKey: cipher.sshKey.map(CipherSSHKeyModel.init), type: CipherType(type: cipher.type) ) } diff --git a/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift b/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift index 380b94b1d..f82fbff9f 100644 --- a/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift +++ b/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift @@ -514,6 +514,11 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length ? { $0.deletedDate == nil } : { $0.deletedDate != nil } + let isSSHKeyVaultItemEnabled: Bool = await configService.getFeatureFlag(.sshKeyVaultItem) + let sshKeyFilter: (CipherView) -> Bool = { cipher in + cipher.type != .sshKey || isSSHKeyVaultItemEnabled + } + return try await cipherService.ciphersPublisher().asyncTryMap { ciphers -> [CipherView] in // Convert the Ciphers to CipherViews and filter appropriately. let matchingCiphers = try await ciphers.asyncMap { cipher in @@ -522,6 +527,7 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length .filter { cipher in filterType.cipherFilter(cipher) && isMatchingCipher(cipher) && + sshKeyFilter(cipher) && (cipherFilter?(cipher) ?? true) } @@ -739,6 +745,8 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length items = activeCiphers.filter { $0.folderId == nil }.compactMap(VaultListItem.init) case .secureNote: items = activeCiphers.filter { $0.type == .secureNote }.compactMap(VaultListItem.init) + case .sshKey: + items = activeCiphers.filter { $0.type == .sshKey }.compactMap(VaultListItem.init) case .totp: items = try await totpListItems(from: activeCiphers, filter: filter) case .trash: @@ -830,7 +838,11 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length .filter(filter.cipherFilter) .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } - let activeCiphers = ciphers.filter { $0.deletedDate == nil } + let isSSHKeyVaultItemFlagEnabled: Bool = await configService.getFeatureFlag(.sshKeyVaultItem) + let activeCiphers = ciphers.filter { cipher in + cipher.deletedDate == nil + && (isSSHKeyVaultItemFlagEnabled || cipher.type != .sshKey) + } let folders = try await clientService.vault().folders() .decryptList(folders: folders) @@ -882,13 +894,18 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length let typesLoginCount = activeCiphers.lazy.filter { $0.type == .login }.count let typesSecureNoteCount = activeCiphers.lazy.filter { $0.type == .secureNote }.count - let types = [ + var types = [ VaultListItem(id: "Types.Logins", itemType: .group(.login, typesLoginCount)), VaultListItem(id: "Types.Cards", itemType: .group(.card, typesCardCount)), VaultListItem(id: "Types.Identities", itemType: .group(.identity, typesIdentityCount)), VaultListItem(id: "Types.SecureNotes", itemType: .group(.secureNote, typesSecureNoteCount)), ] + if isSSHKeyVaultItemFlagEnabled { + let typesSSHKeyCount = activeCiphers.lazy.filter { $0.type == .sshKey }.count + types.append(VaultListItem(id: "Types.SSHKeys", itemType: .group(.sshKey, typesSSHKeyCount))) + } + return [ VaultListSection(id: "TOTP", items: totpItems, name: Localizations.totp), VaultListSection(id: "Favorites", items: ciphersFavorites, name: Localizations.favorites), @@ -1292,6 +1309,8 @@ extension DefaultVaultRepository: VaultRepository { return cipher.folderId == nil case .secureNote: return cipher.type == .secureNote + case .sshKey: + return cipher.type == .sshKey case .totp: return cipher.type == .login && cipher.login?.totp != nil diff --git a/BitwardenShared/Core/Vault/Repositories/VaultRepositoryTests.swift b/BitwardenShared/Core/Vault/Repositories/VaultRepositoryTests.swift index 6cb8c02d0..c012f56a1 100644 --- a/BitwardenShared/Core/Vault/Repositories/VaultRepositoryTests.swift +++ b/BitwardenShared/Core/Vault/Repositories/VaultRepositoryTests.swift @@ -1385,6 +1385,7 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b name: "one time cafefe", type: .login ), + .fixture(id: "6", name: "Some sshkey", type: .sshKey), ] let cipherView = try CipherView(cipher: XCTUnwrap(cipherService.ciphersSubject.value[0])) let expectedSearchResult = try [XCTUnwrap(VaultListItem(cipherView: cipherView))] @@ -1421,6 +1422,7 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b name: "one time cafefe", type: .login ), + .fixture(id: "6", name: "Some sshkey", type: .sshKey), ] let cipherView = try CipherView(cipher: XCTUnwrap(cipherService.ciphersSubject.value[3])) let expectedSearchResult = try [XCTUnwrap(VaultListItem(cipherView: cipherView))] @@ -1462,6 +1464,7 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b name: "one time cafefe", type: .login ), + .fixture(id: "6", name: "Some sshkey", type: .sshKey), ] let cipherView = try CipherView(cipher: XCTUnwrap(cipherService.ciphersSubject.value[4])) let expectedSearchResult = try [XCTUnwrap(VaultListItem(cipherView: cipherView))] @@ -1507,6 +1510,7 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b name: "one time cafefe", type: .login ), + .fixture(id: "6", name: "Some sshkey", type: .sshKey), ] let cipherView = try CipherView(cipher: XCTUnwrap(cipherService.ciphersSubject.value[4])) let expectedSearchResult = try [XCTUnwrap(VaultListItem(cipherView: cipherView))] @@ -1548,6 +1552,7 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b name: "one time cafefe", type: .login ), + .fixture(id: "6", name: "Some sshkey", type: .sshKey), ] let expectedSearchResult = try [ XCTUnwrap( @@ -1599,6 +1604,7 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b name: "one time cafefe", type: .login ), + .fixture(id: "6", name: "Some sshkey", type: .sshKey), ] let expectedSearchResult = try [ XCTUnwrap( @@ -1623,6 +1629,97 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b XCTAssertEqual(ciphers, expectedSearchResult) } + /// `searchVaultListPublisher(searchText:, group: .sshKey, filterType:)` + /// returns search matching cipher name for SSH key items. + @MainActor + func test_searchVaultListPublisher_searchText_sshKey() async throws { + configService.featureFlagsBool[.sshKeyVaultItem] = true + stateService.activeAccount = .fixture() + cipherService.ciphersSubject.value = [ + .fixture(id: "1", name: "café", type: .card), + .fixture(id: "2", name: "cafepass", type: .login), + .fixture(deletedDate: .now, id: "3", name: "deleted Café"), + .fixture( + folderId: "coffee", + id: "0", + name: "Best Cafes", + type: .secureNote + ), + .fixture( + collectionIds: ["123", "meep"], + id: "4", + name: "Café Friend", + type: .identity + ), + .fixture(id: "5", name: "Café thoughts", type: .secureNote), + .fixture( + id: "6", + login: .fixture(totp: .standardTotpKey), + name: "one time cafefe", + type: .login + ), + .fixture(id: "7", name: "cafe", type: .sshKey), + ] + let expectedSearchResult = try [ + XCTUnwrap( + VaultListItem( + cipherView: CipherView(cipher: XCTUnwrap(cipherService.ciphersSubject.value[7])) + ) + ), + ] + var iterator = try await subject + .searchVaultListPublisher( + searchText: "cafe", + group: .sshKey, + filterType: .allVaults + ) + .makeAsyncIterator() + let ciphers = try await iterator.next() + XCTAssertEqual(ciphers, expectedSearchResult) + } + + /// `searchVaultListPublisher(searchText:, group: .sshKey, filterType:)` + /// returns 0 search matching cipher name for SSH key items when `.sshKeyVaultItem` flag is disabled. + @MainActor + func test_searchVaultListPublisher_searchText_sshKeyWithFlagDisabled() async throws { + configService.featureFlagsBool[.sshKeyVaultItem] = false + stateService.activeAccount = .fixture() + cipherService.ciphersSubject.value = [ + .fixture(id: "1", name: "café", type: .card), + .fixture(id: "2", name: "cafepass", type: .login), + .fixture(deletedDate: .now, id: "3", name: "deleted Café"), + .fixture( + folderId: "coffee", + id: "0", + name: "Best Cafes", + type: .secureNote + ), + .fixture( + collectionIds: ["123", "meep"], + id: "4", + name: "Café Friend", + type: .identity + ), + .fixture(id: "5", name: "Café thoughts", type: .secureNote), + .fixture( + id: "6", + login: .fixture(totp: .standardTotpKey), + name: "one time cafefe", + type: .login + ), + .fixture(id: "7", name: "cafe", type: .sshKey), + ] + var iterator = try await subject + .searchVaultListPublisher( + searchText: "cafe", + group: .sshKey, + filterType: .allVaults + ) + .makeAsyncIterator() + let ciphers = try await iterator.next() + XCTAssertEqual(ciphers, []) + } + /// `searchVaultListPublisher(searchText:, group: .totp, filterType:)` /// returns search matching cipher name for TOTP login items. func test_searchVaultListPublisher_searchText_totp() async throws { @@ -2235,6 +2332,26 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b ) } + /// `vaultListPublisher(group:filter:)` returns a publisher for the vault list items on SSH key group. + func test_vaultListPublisher_groups_sshKey() async throws { + let cipher = Cipher.fixture(id: "1", type: .sshKey) + cipherService.ciphersSubject.send([cipher]) + + var iterator = try await subject.vaultListPublisher(group: .sshKey, filter: .allVaults).makeAsyncIterator() + let vaultListSections = try await iterator.next() + + XCTAssertEqual( + vaultListSections, + [ + VaultListSection( + id: "Items", + items: [.fixture(cipherView: .init(cipher: cipher))], + name: Localizations.items + ), + ] + ) + } + /// `vaultListPublisher(group:filter:)` returns a publisher for the vault list items for premium accounts. func test_vaultListPublisher_groups_totp_premium() async throws { stateService.activeAccount = premiumAccount @@ -2682,6 +2799,47 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b } } + /// `vaultListPublisher()` returns a publisher for the list of sections and items that are + /// displayed in the vault for a vault that contains collections and folders, with no filter. + @MainActor + func test_vaultListPublisher_withCollections_allWithSSHKeyFlagEnabled() async throws { + configService.featureFlagsBool[.sshKeyVaultItem] = true + stateService.activeAccount = .fixture() + let syncResponse = try JSONDecoder.defaultDecoder.decode( + SyncResponseModel.self, + from: APITestData.syncWithCiphersCollections.data + ) + cipherService.ciphersSubject.send(syncResponse.ciphers.compactMap(Cipher.init)) + collectionService.collectionsSubject.send(syncResponse.collections.compactMap(Collection.init)) + folderService.foldersSubject.send(syncResponse.folders.compactMap(Folder.init)) + + var iterator = try await subject.vaultListPublisher(filter: .allVaults).makeAsyncIterator() + let sections = try await iterator.next() + + try assertInlineSnapshot(of: dumpVaultListSections(XCTUnwrap(sections)), as: .lines) { + """ + Section: Favorites + - Cipher: Apple + Section: Types + - Group: Login (6) + - Group: Card (1) + - Group: Identity (1) + - Group: Secure note (1) + - Group: SSH key (1) + Section: Folders + - Group: Development (0) + - Group: Internal (1) + - Group: Social (2) + - Group: No Folder (6) + Section: Collections + - Group: Design (2) + - Group: Engineering (3) + Section: Trash + - Group: Trash (1) + """ + } + } + /// `vaultListPublisher()` returns a publisher for the list of sections and items that are /// displayed in the vault for a vault that contains collections with the my vault filter. func test_vaultListPublisher_withCollections_myVault() async throws { @@ -2716,6 +2874,44 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b } } + /// `vaultListPublisher()` returns a publisher for the list of sections and items that are + /// displayed in the vault for a vault that contains collections with the my vault filter with SSH Key flag enabled. + @MainActor + func test_vaultListPublisher_withCollections_myVaultWithSSHKeyFlagEnabled() async throws { + configService.featureFlagsBool[.sshKeyVaultItem] = true + stateService.activeAccount = .fixture() + let syncResponse = try JSONDecoder.defaultDecoder.decode( + SyncResponseModel.self, + from: APITestData.syncWithCiphersCollections.data + ) + cipherService.ciphersSubject.send(syncResponse.ciphers.compactMap(Cipher.init)) + collectionService.collectionsSubject.send(syncResponse.collections.compactMap(Collection.init)) + folderService.foldersSubject.send(syncResponse.folders.compactMap(Folder.init)) + + var iterator = try await subject.vaultListPublisher(filter: .myVault).makeAsyncIterator() + let sections = try await iterator.next() + + try assertInlineSnapshot(of: dumpVaultListSections(XCTUnwrap(sections)), as: .lines) { + """ + Section: Types + - Group: Login (1) + - Group: Card (1) + - Group: Identity (1) + - Group: Secure note (1) + - Group: SSH key (1) + Section: Folders + - Group: Social (1) + Section: No Folder + - Cipher: Bitwarden User + - Cipher: Top Secret Note + - Cipher: Top SSH Key + - Cipher: Visa + Section: Trash + - Group: Trash (1) + """ + } + } + /// `vaultListPublisher()` returns a publisher for the list of sections and items that are /// displayed in the vault for a vault that contains collections with the organization filter. func test_vaultListPublisher_withCollections_organization() async throws { diff --git a/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphers.json b/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphers.json index f1442ba9c..4f6a4e054 100644 --- a/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphers.json +++ b/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphers.json @@ -132,6 +132,24 @@ "collectionIds": [], "creationDate": "2023-07-04T05:00:53Z", "edit": true + }, + { + "sshKey": { + "privateKey": "privateKey", + "publicKey": "publicKey", + "keyFingerprint": "keyFingerprint" + }, + "viewPassword": true, + "id": "98f65684-f059-4cfd-9eb0-947b69b51882", + "favorite": false, + "reprompt": 0, + "name": "Top SSH Key", + "type": 5, + "organizationUseTotp": false, + "revisionDate": "2024-08-13T12:15:17Z", + "collectionIds": [], + "creationDate": "2024-07-04T05:00:53Z", + "edit": true } ], "collections": [], diff --git a/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphersCollections.json b/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphersCollections.json index f22f548c3..2443a25fb 100644 --- a/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphersCollections.json +++ b/BitwardenShared/Core/Vault/Services/API/Fixtures/syncWithCiphersCollections.json @@ -245,6 +245,24 @@ ], "edit": true, "creationDate": "2023-12-01T15:49:55Z" + }, + { + "sshKey": { + "privateKey": "privateKey", + "publicKey": "publicKey", + "keyFingerprint": "keyFingerprint" + }, + "viewPassword": true, + "id": "98f65684-f059-4cfd-9eb0-947b69b51882", + "favorite": false, + "reprompt": 0, + "name": "Top SSH Key", + "type": 5, + "organizationUseTotp": false, + "revisionDate": "2024-08-13T12:15:17Z", + "collectionIds": [], + "creationDate": "2024-07-04T05:00:53Z", + "edit": true } ], "collections": [ diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings index bbf132ffd..b4f3f8608 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings @@ -1058,3 +1058,4 @@ "CopyPublicKey" = "Copy public key"; "CopyPrivateKey" = "Copy private key"; "CopyFingerprint" = "Copy fingerprint"; +"SSHKeys" = "SSH keys"; diff --git a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift index 3a926ede9..7a261774d 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift @@ -63,7 +63,11 @@ struct VaultGroupState: Equatable, Sendable { // If the collection or trash are empty, return false. if case .collection = group { return false - } else if case .trash = group { + } + if case .sshKey = group { + return false + } + if case .trash = group { return false } return true @@ -71,6 +75,9 @@ struct VaultGroupState: Equatable, Sendable { /// Whether to show the add item floating action button. var showAddItemFloatingActionButton: Bool { + if case .sshKey = group { + return false + } if case .trash = group { return false } diff --git a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupViewTests.swift b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupViewTests.swift index ba36205d7..85fd8ca2e 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupViewTests.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupViewTests.swift @@ -133,6 +133,13 @@ class VaultGroupViewTests: BitwardenTestCase { assertSnapshot(of: subject, as: .defaultPortrait) } + @MainActor + func test_snapshot_emptySSHKey() { + processor.state.group = .sshKey + processor.state.loadingState = .data([]) + assertSnapshot(of: subject, as: .defaultPortrait) + } + @MainActor func test_snapshot_emptyTrash() { processor.state.group = .trash diff --git a/BitwardenShared/UI/Vault/Vault/VaultGroup/__Snapshots__/VaultGroupViewTests/test_snapshot_emptySSHKey.1.png b/BitwardenShared/UI/Vault/Vault/VaultGroup/__Snapshots__/VaultGroupViewTests/test_snapshot_emptySSHKey.1.png new file mode 100644 index 0000000000000000000000000000000000000000..b96465cbd1dae5e995d661f7514eb973cef2ecd2 GIT binary patch literal 74327 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 Date: Thu, 31 Oct 2024 16:11:18 -0300 Subject: [PATCH 2/2] PM-10401 Fixed key24 image and moved to switch logic in VaultGroupState. --- .../Icons/key24.imageset/Contents.json | 16 +++++++++++++ .../Icons/key24.imageset/key24.pdf | Bin 0 -> 2469 bytes .../Vault/VaultGroup/VaultGroupState.swift | 22 +++++++----------- .../Vault/Vault/VaultList/VaultListItem.swift | 4 ++-- .../Vault/VaultList/VaultListItemTests.swift | 4 ++-- 5 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/Contents.json create mode 100644 BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/key24.pdf diff --git a/BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/Contents.json b/BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/Contents.json new file mode 100644 index 000000000..19c3c6b1a --- /dev/null +++ b/BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "key24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/key24.pdf b/BitwardenShared/UI/Platform/Application/Support/Images.xcassets/Icons/key24.imageset/key24.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cc1b5ce24648d58fde72d1ff6f8873f5de276319 GIT binary patch literal 2469 zcmY!laBP1$s zYuh5pHpx@pizKhc221q+o5^W$SM0{Rz70qID6*GF8$4WdIg7)j;NZ$V2-%Nx~AlWY-hLRpF?aP zKe-rpT`=OQ30}DR{N(@nw}IjR{9EzwrqUn6>}8L{pU?NcCptB3;iCWfI|EmV?>X2V z6LG7vkl~ir5vkU@N& z?3Z0n4>Qb>I5ul_>YS7W?#<2pSM@XuwO&1T)yP{p;ofP9#nlI=oT~hockAS_EhQ5Z z=2!Z?xl+7=|Dl3>rbK*hWI)*}%Z1M0TC2|7lkcjuHQ-9kOMxa&APbZ^xjGW4I_N@8Zd!CPO%f7IGQFGBG@Z>%v)ZIU|@NI3qci3$6^4QJkEIkT!zL=W-#~ z2v!b~C|qd{LO(V!WZfBDX3oce83>82{ zb`YZUbFC9LogNWn^>F;WKFpB1&UamcF|JC|NI&q5S1P%S-@&q=lr~q)I6Y#pb&rw27oHm5(P`B5K=gSgdjcu zIo3Nf1?W@-kPm_sU@msfF9qr~#BhHxPz1>&Ljx3}Kt3#n+lcVFb7DziPJTM7-io5s zG%f=LLqje|RShB)%uG#Umn5rzyfVm9CEF%MBbVnE&8X(LnDN4-DNiE_61+S+IFmN@B^K(-*6*Mwa uG$BE(AC#XD3|~<2f#XX*II}7h6c)h1EiOqcDglR%k)fFdm#V6(zZ(E<2H!^j literal 0 HcmV?d00001 diff --git a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift index 7a261774d..ee04f140d 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupState.swift @@ -60,28 +60,22 @@ struct VaultGroupState: Equatable, Sendable { // Don't show if there is data. guard emptyData else { return false } - // If the collection or trash are empty, return false. - if case .collection = group { - return false - } - if case .sshKey = group { - return false - } - if case .trash = group { + switch group { + case .collection, .sshKey, .trash: return false + default: + return true } - return true } /// Whether to show the add item floating action button. var showAddItemFloatingActionButton: Bool { - if case .sshKey = group { - return false - } - if case .trash = group { + switch group { + case .sshKey, .trash: return false + default: + return true } - return true } /// Whether to show the special web icons. diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift index 70cc7ab43..fd9e3b105 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift @@ -94,7 +94,7 @@ extension VaultListItem { case .secureNote: Asset.Images.file24 case .sshKey: - Asset.Images.key16 + Asset.Images.key24 } case let .group(group, _): switch group { @@ -112,7 +112,7 @@ extension VaultListItem { case .secureNote: Asset.Images.file24 case .sshKey: - Asset.Images.key16 + Asset.Images.key24 case .totp: Asset.Images.clock24 case .trash: diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItemTests.swift b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItemTests.swift index 7ee702ab7..cbf894fd6 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItemTests.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItemTests.swift @@ -144,7 +144,7 @@ class VaultListItemTests: BitwardenTestCase { // swiftlint:disable:this type_bod ) XCTAssertEqual( VaultListItem(cipherView: .fixture(type: .sshKey))?.icon.name, - Asset.Images.key16.name + Asset.Images.key24.name ) XCTAssertEqual( @@ -173,7 +173,7 @@ class VaultListItemTests: BitwardenTestCase { // swiftlint:disable:this type_bod ) XCTAssertEqual( VaultListItem(id: "", itemType: .group(.sshKey, 1)).icon.name, - Asset.Images.key16.name + Asset.Images.key24.name ) XCTAssertEqual( VaultListItem(id: "", itemType: .group(.totp, 1)).icon.name,