mirror of
				https://github.com/maxgoedjen/secretive.git
				synced 2025-11-04 01:10:56 +00:00 
			
		
		
		
	Allow reload pre-op
This commit is contained in:
		
							parent
							
								
									382913cb99
								
							
						
					
					
						commit
						8c6c54ddd3
					
				@ -31,13 +31,14 @@ public class Agent {
 | 
				
			|||||||
    private let writer = OpenSSHKeyWriter()
 | 
					    private let writer = OpenSSHKeyWriter()
 | 
				
			||||||
    private let requestTracer = SigningRequestTracer()
 | 
					    private let requestTracer = SigningRequestTracer()
 | 
				
			||||||
    private let certsPath = (NSHomeDirectory() as NSString).appendingPathComponent("PublicKeys") as String
 | 
					    private let certsPath = (NSHomeDirectory() as NSString).appendingPathComponent("PublicKeys") as String
 | 
				
			||||||
 | 
					    private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent.agent", category: "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Initializes an agent with a store list and a witness.
 | 
					    /// Initializes an agent with a store list and a witness.
 | 
				
			||||||
    /// - Parameters:
 | 
					    /// - Parameters:
 | 
				
			||||||
    ///   - storeList: The `SecretStoreList` to make available.
 | 
					    ///   - storeList: The `SecretStoreList` to make available.
 | 
				
			||||||
    ///   - witness: A witness to notify of requests.
 | 
					    ///   - witness: A witness to notify of requests.
 | 
				
			||||||
    public init(storeList: SecretStoreList, witness: SigningWitness? = nil) {
 | 
					    public init(storeList: SecretStoreList, witness: SigningWitness? = nil) {
 | 
				
			||||||
        Logger().debug("Agent is running")
 | 
					        logger.debug("Agent is running")
 | 
				
			||||||
        self.storeList = storeList
 | 
					        self.storeList = storeList
 | 
				
			||||||
        self.witness = witness
 | 
					        self.witness = witness
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -53,16 +54,16 @@ extension Agent {
 | 
				
			|||||||
    /// - Return value: 
 | 
					    /// - Return value: 
 | 
				
			||||||
    ///   - Boolean if data could be read
 | 
					    ///   - Boolean if data could be read
 | 
				
			||||||
    @discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool {
 | 
					    @discardableResult public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool {
 | 
				
			||||||
        Logger().debug("Agent handling new data")
 | 
					        logger.debug("Agent handling new data")
 | 
				
			||||||
        let data = Data(reader.availableData)
 | 
					        let data = Data(reader.availableData)
 | 
				
			||||||
        guard data.count > 4 else { return false}
 | 
					        guard data.count > 4 else { return false}
 | 
				
			||||||
        let requestTypeInt = data[4]
 | 
					        let requestTypeInt = data[4]
 | 
				
			||||||
        guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
 | 
					        guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
 | 
				
			||||||
            writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
 | 
					            writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
 | 
				
			||||||
            Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
 | 
					            logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
 | 
				
			||||||
            return true
 | 
					            return true
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Logger().debug("Agent handling request of type \(requestType.debugDescription)")
 | 
					        logger.debug("Agent handling request of type \(requestType.debugDescription)")
 | 
				
			||||||
        let subData = Data(data[5...])
 | 
					        let subData = Data(data[5...])
 | 
				
			||||||
        let response = handle(requestType: requestType, data: subData, reader: reader)
 | 
					        let response = handle(requestType: requestType, data: subData, reader: reader)
 | 
				
			||||||
        writer.write(response)
 | 
					        writer.write(response)
 | 
				
			||||||
@ -70,23 +71,25 @@ extension Agent {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
 | 
					    func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
 | 
				
			||||||
 | 
					        // Depending on the launch context (such as after macOS update), the agent may need to reload secrets before acting
 | 
				
			||||||
 | 
					        reloadSecretsIfNeccessary()
 | 
				
			||||||
        var response = Data()
 | 
					        var response = Data()
 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
            switch requestType {
 | 
					            switch requestType {
 | 
				
			||||||
            case .requestIdentities:
 | 
					            case .requestIdentities:
 | 
				
			||||||
                response.append(SSHAgent.ResponseType.agentIdentitiesAnswer.data)
 | 
					                response.append(SSHAgent.ResponseType.agentIdentitiesAnswer.data)
 | 
				
			||||||
                response.append(identities())
 | 
					                response.append(identities())
 | 
				
			||||||
                Logger().debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)")
 | 
					                logger.debug("Agent returned \(SSHAgent.ResponseType.agentIdentitiesAnswer.debugDescription)")
 | 
				
			||||||
            case .signRequest:
 | 
					            case .signRequest:
 | 
				
			||||||
                let provenance = requestTracer.provenance(from: reader)
 | 
					                let provenance = requestTracer.provenance(from: reader)
 | 
				
			||||||
                response.append(SSHAgent.ResponseType.agentSignResponse.data)
 | 
					                response.append(SSHAgent.ResponseType.agentSignResponse.data)
 | 
				
			||||||
                response.append(try sign(data: data, provenance: provenance))
 | 
					                response.append(try sign(data: data, provenance: provenance))
 | 
				
			||||||
                Logger().debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)")
 | 
					                logger.debug("Agent returned \(SSHAgent.ResponseType.agentSignResponse.debugDescription)")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch {
 | 
					        } catch {
 | 
				
			||||||
            response.removeAll()
 | 
					            response.removeAll()
 | 
				
			||||||
            response.append(SSHAgent.ResponseType.agentFailure.data)
 | 
					            response.append(SSHAgent.ResponseType.agentFailure.data)
 | 
				
			||||||
            Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
 | 
					            logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let full = OpenSSHKeyWriter().lengthAndData(of: response)
 | 
					        let full = OpenSSHKeyWriter().lengthAndData(of: response)
 | 
				
			||||||
        return full
 | 
					        return full
 | 
				
			||||||
@ -120,7 +123,7 @@ extension Agent {
 | 
				
			|||||||
            keyData.append(writer.lengthAndData(of: curveData))
 | 
					            keyData.append(writer.lengthAndData(of: curveData))
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Logger().debug("Agent enumerated \(secrets.count) identities")
 | 
					        logger.log("Agent enumerated \(secrets.count) identities")
 | 
				
			||||||
        return countData + keyData
 | 
					        return countData + keyData
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -139,7 +142,7 @@ extension Agent {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        guard let (store, secret) = secret(matching: hash) else {
 | 
					        guard let (store, secret) = secret(matching: hash) else {
 | 
				
			||||||
            Logger().debug("Agent did not have a key matching \(hash as NSData)")
 | 
					            logger.debug("Agent did not have a key matching \(hash as NSData)")
 | 
				
			||||||
            throw AgentError.noMatchingKey
 | 
					            throw AgentError.noMatchingKey
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -193,7 +196,7 @@ extension Agent {
 | 
				
			|||||||
            try witness.witness(accessTo: secret, from: store, by: provenance)
 | 
					            try witness.witness(accessTo: secret, from: store, by: provenance)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Logger().debug("Agent signed request")
 | 
					        logger.debug("Agent signed request")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return signedData
 | 
					        return signedData
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -235,7 +238,7 @@ extension Agent {
 | 
				
			|||||||
        let certificatePath = certsPath.appending("/").appending("\(minimalHex)-cert.pub")
 | 
					        let certificatePath = certsPath.appending("/").appending("\(minimalHex)-cert.pub")
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if FileManager.default.fileExists(atPath: certificatePath) {
 | 
					        if FileManager.default.fileExists(atPath: certificatePath) {
 | 
				
			||||||
            Logger().debug("Found certificate for \(secret.name)")
 | 
					            logger.debug("Found certificate for \(secret.name)")
 | 
				
			||||||
            do {
 | 
					            do {
 | 
				
			||||||
                let certContent = try String(contentsOfFile:certificatePath, encoding: .utf8)
 | 
					                let certContent = try String(contentsOfFile:certificatePath, encoding: .utf8)
 | 
				
			||||||
                let certElements = certContent.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ")
 | 
					                let certElements = certContent.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ")
 | 
				
			||||||
@ -246,19 +249,19 @@ extension Agent {
 | 
				
			|||||||
                            if let certName = certElements[2].data(using: .utf8) {
 | 
					                            if let certName = certElements[2].data(using: .utf8) {
 | 
				
			||||||
                                return (certDecoded, certName)
 | 
					                                return (certDecoded, certName)
 | 
				
			||||||
                            } else if let certName = secret.name.data(using: .utf8) {
 | 
					                            } else if let certName = secret.name.data(using: .utf8) {
 | 
				
			||||||
                                Logger().info("Certificate for \(secret.name) does not have a name tag, using secret name instead")
 | 
					                                logger.info("Certificate for \(secret.name) does not have a name tag, using secret name instead")
 | 
				
			||||||
                                return (certDecoded, certName)
 | 
					                                return (certDecoded, certName)
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                throw OpenSSHCertificateError.parsingFailed
 | 
					                                throw OpenSSHCertificateError.parsingFailed
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        Logger().warning("Certificate found for \(secret.name) but failed to decode base64 key")
 | 
					                        logger.warning("Certificate found for \(secret.name) but failed to decode base64 key")
 | 
				
			||||||
                        throw OpenSSHCertificateError.parsingFailed
 | 
					                        throw OpenSSHCertificateError.parsingFailed
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } catch {
 | 
					            } catch {
 | 
				
			||||||
                Logger().warning("Certificate found for \(secret.name) but failed to load")
 | 
					                logger.warning("Certificate found for \(secret.name) but failed to load")
 | 
				
			||||||
                throw OpenSSHCertificateError.parsingFailed
 | 
					                throw OpenSSHCertificateError.parsingFailed
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -270,6 +273,16 @@ extension Agent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
extension Agent {
 | 
					extension Agent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gives any store with no loaded secrets a chance to reload.
 | 
				
			||||||
 | 
					    func reloadSecretsIfNeccessary() {
 | 
				
			||||||
 | 
					        for store in storeList.stores {
 | 
				
			||||||
 | 
					            if store.secrets.isEmpty {
 | 
				
			||||||
 | 
					                logger.debug("Store \(store.name, privacy: .public) has no loaded secrets. Reloading.")
 | 
				
			||||||
 | 
					                store.reloadSecrets()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Finds a ``Secret`` matching a specified hash whos signature was requested.
 | 
					    /// Finds a ``Secret`` matching a specified hash whos signature was requested.
 | 
				
			||||||
    /// - Parameter hash: The hash to match against.
 | 
					    /// - Parameter hash: The hash to match against.
 | 
				
			||||||
    /// - Returns: A ``Secret`` and the ``SecretStore`` containing it, if a match is found.
 | 
					    /// - Returns: A ``Secret`` and the ``SecretStore`` containing it, if a match is found.
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ public class AnySecretStore: SecretStore {
 | 
				
			|||||||
    private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data
 | 
					    private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data
 | 
				
			||||||
    private let _existingPersistedAuthenticationContext: (AnySecret) -> PersistedAuthenticationContext?
 | 
					    private let _existingPersistedAuthenticationContext: (AnySecret) -> PersistedAuthenticationContext?
 | 
				
			||||||
    private let _persistAuthentication: (AnySecret, TimeInterval) throws -> Void
 | 
					    private let _persistAuthentication: (AnySecret, TimeInterval) throws -> Void
 | 
				
			||||||
 | 
					    private let _reloadSecrets: () -> Void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var sink: AnyCancellable?
 | 
					    private var sink: AnyCancellable?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,6 +25,7 @@ public class AnySecretStore: SecretStore {
 | 
				
			|||||||
        _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) }
 | 
					        _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) }
 | 
				
			||||||
        _existingPersistedAuthenticationContext = { secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
 | 
					        _existingPersistedAuthenticationContext = { secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
 | 
				
			||||||
        _persistAuthentication = { try secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) }
 | 
					        _persistAuthentication = { try secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) }
 | 
				
			||||||
 | 
					        _reloadSecrets = { secretStore.reloadSecrets() }
 | 
				
			||||||
        sink = secretStore.objectWillChange.sink { _ in
 | 
					        sink = secretStore.objectWillChange.sink { _ in
 | 
				
			||||||
            self.objectWillChange.send()
 | 
					            self.objectWillChange.send()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -57,6 +59,10 @@ public class AnySecretStore: SecretStore {
 | 
				
			|||||||
        try _persistAuthentication(secret, duration)
 | 
					        try _persistAuthentication(secret, duration)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public func reloadSecrets() {
 | 
				
			||||||
 | 
					        _reloadSecrets()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
 | 
					public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
 | 
				
			||||||
 | 
				
			|||||||
@ -36,6 +36,9 @@ public protocol SecretStore: ObservableObject, Identifiable {
 | 
				
			|||||||
    ///  - Note: This is used for temporarily unlocking access to a secret which would otherwise require authentication every single use. This is useful for situations where the user anticipates several rapid accesses to a authorization-guarded secret.
 | 
					    ///  - Note: This is used for temporarily unlocking access to a secret which would otherwise require authentication every single use. This is useful for situations where the user anticipates several rapid accesses to a authorization-guarded secret.
 | 
				
			||||||
    func persistAuthentication(secret: SecretType, forDuration duration: TimeInterval) throws
 | 
					    func persistAuthentication(secret: SecretType, forDuration duration: TimeInterval) throws
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Requests that the store reload secrets from any backing store, if neccessary.
 | 
				
			||||||
 | 
					    func reloadSecrets()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A SecretStore that the Secretive admin app can modify.
 | 
					/// A SecretStore that the Secretive admin app can modify.
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ extension SecureEnclave {
 | 
				
			|||||||
        /// Initializes a Store.
 | 
					        /// Initializes a Store.
 | 
				
			||||||
        public init() {
 | 
					        public init() {
 | 
				
			||||||
            DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
 | 
					            DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
 | 
				
			||||||
                self.reloadSecrets(notifyAgent: false)
 | 
					                self.reloadSecretsInternal(notifyAgent: false)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            loadSecrets()
 | 
					            loadSecrets()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -68,7 +68,7 @@ extension SecureEnclave {
 | 
				
			|||||||
                throw KeychainError(statusCode: nil)
 | 
					                throw KeychainError(statusCode: nil)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            try savePublicKey(publicKey, name: name)
 | 
					            try savePublicKey(publicKey, name: name)
 | 
				
			||||||
            reloadSecrets()
 | 
					            reloadSecretsInternal()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public func delete(secret: Secret) throws {
 | 
					        public func delete(secret: Secret) throws {
 | 
				
			||||||
@ -80,7 +80,7 @@ extension SecureEnclave {
 | 
				
			|||||||
            if status != errSecSuccess {
 | 
					            if status != errSecSuccess {
 | 
				
			||||||
                throw KeychainError(statusCode: status)
 | 
					                throw KeychainError(statusCode: status)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            reloadSecrets()
 | 
					            reloadSecretsInternal()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public func update(secret: Secret, name: String) throws {
 | 
					        public func update(secret: Secret, name: String) throws {
 | 
				
			||||||
@ -97,7 +97,7 @@ extension SecureEnclave {
 | 
				
			|||||||
            if status != errSecSuccess {
 | 
					            if status != errSecSuccess {
 | 
				
			||||||
                throw KeychainError(statusCode: status)
 | 
					                throw KeychainError(statusCode: status)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            reloadSecrets()
 | 
					            reloadSecretsInternal()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data {
 | 
					        public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data {
 | 
				
			||||||
@ -163,6 +163,10 @@ extension SecureEnclave {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public func reloadSecrets() {
 | 
				
			||||||
 | 
					            reloadSecretsInternal()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -171,7 +175,7 @@ extension SecureEnclave.Store {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Reloads all secrets from the store.
 | 
					    /// Reloads all secrets from the store.
 | 
				
			||||||
    /// - Parameter notifyAgent: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well.
 | 
					    /// - Parameter notifyAgent: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well.
 | 
				
			||||||
    private func reloadSecrets(notifyAgent: Bool = true) {
 | 
					    private func reloadSecretsInternal(notifyAgent: Bool = true) {
 | 
				
			||||||
        secrets.removeAll()
 | 
					        secrets.removeAll()
 | 
				
			||||||
        loadSecrets()
 | 
					        loadSecrets()
 | 
				
			||||||
        NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
 | 
					        NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
 | 
				
			||||||
 | 
				
			|||||||
@ -89,6 +89,15 @@ extension SmartCard {
 | 
				
			|||||||
        public func persistAuthentication(secret: SmartCard.Secret, forDuration: TimeInterval) throws {
 | 
					        public func persistAuthentication(secret: SmartCard.Secret, forDuration: TimeInterval) throws {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Reloads all secrets from the store.
 | 
				
			||||||
 | 
					        public func reloadSecrets() {
 | 
				
			||||||
 | 
					            DispatchQueue.main.async {
 | 
				
			||||||
 | 
					                self.isAvailable = self.tokenID != nil
 | 
				
			||||||
 | 
					                self.secrets.removeAll()
 | 
				
			||||||
 | 
					                self.loadSecrets()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -102,15 +111,6 @@ extension SmartCard.Store {
 | 
				
			|||||||
        reloadSecrets()
 | 
					        reloadSecrets()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Reloads all secrets from the store.
 | 
					 | 
				
			||||||
    private func reloadSecrets() {
 | 
					 | 
				
			||||||
        DispatchQueue.main.async {
 | 
					 | 
				
			||||||
            self.isAvailable = self.tokenID != nil
 | 
					 | 
				
			||||||
            self.secrets.removeAll()
 | 
					 | 
				
			||||||
            self.loadSecrets()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Loads all secrets from the store.
 | 
					    /// Loads all secrets from the store.
 | 
				
			||||||
    private func loadSecrets() {
 | 
					    private func loadSecrets() {
 | 
				
			||||||
        guard let tokenID = tokenID else { return }
 | 
					        guard let tokenID = tokenID else { return }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user