mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-10 17:46:07 -06:00
155 lines
6.1 KiB
Swift
155 lines
6.1 KiB
Swift
import BitwardenKit
|
|
import BitwardenSdk
|
|
import Foundation
|
|
|
|
/// The Fido2 credential store implementation that the SDK needs
|
|
/// which handles getting/saving credentials for Fido2 flows.
|
|
final class Fido2CredentialStoreService: Fido2CredentialStore {
|
|
// MARK: Properties
|
|
|
|
/// The service used to manage syncing and updates to the user's ciphers.
|
|
private let cipherService: CipherService
|
|
|
|
/// The service that handles common client functionality such as encryption and decryption.
|
|
private let clientService: ClientService
|
|
|
|
/// The service used by the application to report non-fatal errors.
|
|
private let errorReporter: ErrorReporter
|
|
|
|
/// The service used to handle syncing vault data with the API
|
|
private let syncService: SyncService
|
|
|
|
// MARK: Initialization
|
|
|
|
/// Initializes a `Fido2CredentialStoreService`
|
|
/// - Parameters:
|
|
/// - cipherService: The service used to manage syncing and updates to the user's ciphers.
|
|
/// - clientService: The service that handles common client functionality such as encryption and decryption.
|
|
/// - errorReporter: The service used by the application to report non-fatal errors.
|
|
/// - syncService: The service used to handle syncing vault data with the API.
|
|
init(
|
|
cipherService: CipherService,
|
|
clientService: ClientService,
|
|
errorReporter: ErrorReporter,
|
|
syncService: SyncService,
|
|
) {
|
|
self.cipherService = cipherService
|
|
self.clientService = clientService
|
|
self.errorReporter = errorReporter
|
|
self.syncService = syncService
|
|
}
|
|
|
|
/// Gets all the active login ciphers that have Fido2 credentials.
|
|
/// - Returns: Array of active login ciphers that have Fido2 credentials.
|
|
func allCredentials() async throws -> [BitwardenSdk.CipherListView] {
|
|
try await clientService.vault().ciphers().decryptList(
|
|
ciphers: cipherService.fetchAllCiphers().filter(\.isActiveWithFido2Credentials),
|
|
)
|
|
}
|
|
|
|
/// Finds active login ciphers that have Fido2 credentials, match the `ripId` and if `ids` is sent
|
|
/// then filters the one which the Fido2 `credentialId` matches some of the one in `ids`.
|
|
/// - Parameters:
|
|
/// - ids: An array of possible `credentialId` to filter credentials that matches one of them.
|
|
/// When `nil` the `credentialId` filter is not applied.
|
|
/// - ripId: The `ripId` to match the Fido2 credential `rpId`.
|
|
/// - Returns: All the ciphers that matches the filter.
|
|
func findCredentials(ids: [Data]?, ripId: String) async throws -> [BitwardenSdk.CipherView] {
|
|
do {
|
|
try await syncService.fetchSync(forceSync: false)
|
|
} catch {
|
|
errorReporter.log(error: error)
|
|
}
|
|
|
|
let activeCiphersWithFido2Credentials = try await cipherService.fetchAllCiphers()
|
|
.filter(\.isActiveWithFido2Credentials)
|
|
.asyncMap { cipher in
|
|
try await self.clientService.vault().ciphers().decrypt(cipher: cipher)
|
|
}
|
|
|
|
var result = [BitwardenSdk.CipherView]()
|
|
for cipherView in activeCiphersWithFido2Credentials {
|
|
let fido2CredentialAutofillViews = try await clientService.platform()
|
|
.fido2()
|
|
.decryptFido2AutofillCredentials(cipherView: cipherView)
|
|
|
|
guard let fido2CredentialAutofillView = fido2CredentialAutofillViews[safeIndex: 0],
|
|
ripId == fido2CredentialAutofillView.rpId else {
|
|
continue
|
|
}
|
|
|
|
if let ids,
|
|
!ids.contains(fido2CredentialAutofillView.credentialId) {
|
|
continue
|
|
}
|
|
|
|
result.append(cipherView)
|
|
}
|
|
return result
|
|
}
|
|
|
|
/// Saves a cipher credential that contains a Fido2 credential, either creating it or updating it to server.
|
|
/// - Parameter cred: Cipher/Credential to add/update.
|
|
func saveCredential(cred: BitwardenSdk.EncryptionContext) async throws {
|
|
if cred.cipher.id == nil {
|
|
try await cipherService.addCipherWithServer(cred.cipher, encryptedFor: cred.encryptedFor)
|
|
} else {
|
|
try await cipherService.updateCipherWithServer(cred.cipher, encryptedFor: cred.encryptedFor)
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Cipher {
|
|
/// Whether the cipher is active, is a login and has Fido2 credentials.
|
|
var isActiveWithFido2Credentials: Bool {
|
|
deletedDate == nil
|
|
&& type == .login
|
|
&& login?.fido2Credentials?.isEmpty == false
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
|
|
/// A wrapper of a `Fido2CredentialStore` which adds debugging info for the `Fido2DebugginReportBuilder`.
|
|
class DebuggingFido2CredentialStoreService: Fido2CredentialStore {
|
|
let fido2CredentialStore: Fido2CredentialStore
|
|
|
|
init(fido2CredentialStore: Fido2CredentialStore) {
|
|
self.fido2CredentialStore = fido2CredentialStore
|
|
}
|
|
|
|
func findCredentials(ids: [Data]?, ripId: String) async throws -> [BitwardenSdk.CipherView] {
|
|
do {
|
|
let result = try await fido2CredentialStore.findCredentials(ids: ids, ripId: ripId)
|
|
Fido2DebuggingReportBuilder.builder.withFindCredentialsResult(.success(result))
|
|
return result
|
|
} catch {
|
|
Fido2DebuggingReportBuilder.builder.withFindCredentialsResult(.failure(error))
|
|
throw error
|
|
}
|
|
}
|
|
|
|
func allCredentials() async throws -> [BitwardenSdk.CipherListView] {
|
|
do {
|
|
let result = try await fido2CredentialStore.allCredentials()
|
|
Fido2DebuggingReportBuilder.builder.withAllCredentialsResult(.success(result))
|
|
return result
|
|
} catch {
|
|
Fido2DebuggingReportBuilder.builder.withFindCredentialsResult(.failure(error))
|
|
throw error
|
|
}
|
|
}
|
|
|
|
func saveCredential(cred: BitwardenSdk.EncryptionContext) async throws {
|
|
do {
|
|
try await fido2CredentialStore.saveCredential(cred: cred)
|
|
Fido2DebuggingReportBuilder.builder.withSaveCredentialCipher(.success(cred.cipher))
|
|
} catch {
|
|
Fido2DebuggingReportBuilder.builder.withFindCredentialsResult(.failure(error))
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|