amplify-swift/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/CredentialStorage/CredentialStoreOperationCli...

106 lines
3.6 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Amplify
import Foundation
@_spi(KeychainStore) import AWSPluginsCore
protocol CredentialStoreStateBehavior {
func fetchData(type: CredentialStoreDataType) async throws -> CredentialStoreData
func storeData(data: CredentialStoreData) async throws
func deleteData(type: CredentialStoreDataType) async throws
}
class CredentialStoreOperationClient: CredentialStoreStateBehavior {
private let credentialStoreStateMachine: CredentialStoreStateMachine
// Task queue is being used to manage CRUD operations to the credential store synchronously
// This will help us keeping the CRUD methods atomic
private let taskQueue = TaskQueue<CredentialStoreData?>()
init(credentialStoreStateMachine: CredentialStoreStateMachine) {
self.credentialStoreStateMachine = credentialStoreStateMachine
}
func fetchData(type: CredentialStoreDataType) async throws -> CredentialStoreData {
guard let credentialStoreData = try await taskQueue.sync(block: {
await self.waitForValidState()
let credentialStoreEvent = CredentialStoreEvent(
eventType: .loadCredentialStore(type))
return try await self.sendEventAndListenToStateChanges(event: credentialStoreEvent)
}) else {
throw KeychainStoreError.itemNotFound
}
return credentialStoreData
}
func storeData(data: CredentialStoreData) async throws {
_ = try await taskQueue.sync {
await self.waitForValidState()
let credentialStoreEvent = CredentialStoreEvent(
eventType: .storeCredentials(data))
return try await self.sendEventAndListenToStateChanges(event: credentialStoreEvent)
}
}
func deleteData(type: CredentialStoreDataType) async throws {
_ = try await taskQueue.sync {
await self.waitForValidState()
let credentialStoreEvent = CredentialStoreEvent(
eventType: .clearCredentialStore(type))
try await self.sendDeleteEventAndListenToStateChanges(event: credentialStoreEvent)
return nil
}
}
func sendEventAndListenToStateChanges(event: CredentialStoreEvent) async throws -> CredentialStoreData {
let stateSequences = await credentialStoreStateMachine.listen()
await credentialStoreStateMachine.send(event)
for await state in stateSequences {
switch state {
case .success(let credentialStoreData):
return credentialStoreData
case .error(let error):
throw error
default: continue
}
}
throw KeychainStoreError.unknown("Could not complete the operation")
}
func sendDeleteEventAndListenToStateChanges(event: CredentialStoreEvent) async throws {
let stateSequences = await credentialStoreStateMachine.listen()
await credentialStoreStateMachine.send(event)
for await state in stateSequences {
switch state {
case .clearedCredential:
return
case .error(let error):
throw error
default: continue
}
}
}
func waitForValidState() async {
let stateSequences = await credentialStoreStateMachine.listen()
for await state in stateSequences {
switch state {
case .idle, .notConfigured:
return
default: continue
}
}
}
}