mirror of
https://github.com/maxgoedjen/secretive.git
synced 2024-11-22 05:27:28 +00:00
Availibilty
This commit is contained in:
parent
734df29065
commit
a9d7e7644e
@ -3,6 +3,7 @@ import Combine
|
|||||||
public protocol SecretStore: ObservableObject {
|
public protocol SecretStore: ObservableObject {
|
||||||
|
|
||||||
associatedtype SecretType: Secret
|
associatedtype SecretType: Secret
|
||||||
|
var isAvailable: Bool { get }
|
||||||
var name: String { get }
|
var name: String { get }
|
||||||
var secrets: [SecretType] { get }
|
var secrets: [SecretType] { get }
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ extension NSNotification.Name {
|
|||||||
public class AnySecretStore: SecretStore {
|
public class AnySecretStore: SecretStore {
|
||||||
|
|
||||||
fileprivate let base: Any
|
fileprivate let base: Any
|
||||||
|
fileprivate let _isAvailable: () -> Bool
|
||||||
fileprivate let _name: () -> String
|
fileprivate let _name: () -> String
|
||||||
fileprivate let _secrets: () -> [AnySecret]
|
fileprivate let _secrets: () -> [AnySecret]
|
||||||
fileprivate let _sign: (Data, AnySecret) throws -> Data
|
fileprivate let _sign: (Data, AnySecret) throws -> Data
|
||||||
@ -27,12 +29,17 @@ public class AnySecretStore: SecretStore {
|
|||||||
|
|
||||||
public init<T>(_ secretStore: T) where T: SecretStore {
|
public init<T>(_ secretStore: T) where T: SecretStore {
|
||||||
base = secretStore
|
base = secretStore
|
||||||
|
_isAvailable = { secretStore.isAvailable }
|
||||||
_name = { secretStore.name }
|
_name = { secretStore.name }
|
||||||
_secrets = { secretStore.secrets.map { AnySecret($0) } }
|
_secrets = { secretStore.secrets.map { AnySecret($0) } }
|
||||||
_sign = { try secretStore.sign(data: $0, with: $1 as! T.SecretType) }
|
_sign = { try secretStore.sign(data: $0, with: $1 as! T.SecretType) }
|
||||||
_delete = { try secretStore.delete(secret: $0 as! T.SecretType) }
|
_delete = { try secretStore.delete(secret: $0 as! T.SecretType) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isAvailable: Bool {
|
||||||
|
return _isAvailable()
|
||||||
|
}
|
||||||
|
|
||||||
public var name: String {
|
public var name: String {
|
||||||
return _name()
|
return _name()
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Security
|
import Security
|
||||||
|
import CryptoTokenKit
|
||||||
|
|
||||||
extension SecureEnclave {
|
extension SecureEnclave {
|
||||||
|
|
||||||
public class Store: SecretStore {
|
public class Store: SecretStore {
|
||||||
|
|
||||||
|
public var isAvailable: Bool {
|
||||||
|
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
|
||||||
|
// error msg "Received error sending GET UNIQUE DEVICE command"
|
||||||
|
// Verify it with TKTokenWatcher manually.
|
||||||
|
return TKTokenWatcher().tokenIDs.contains("com.apple.setoken")
|
||||||
|
}
|
||||||
public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave")
|
public let name = NSLocalizedString("Secure Enclave", comment: "Secure Enclave")
|
||||||
@Published public fileprivate(set) var secrets: [Secret] = []
|
@Published public fileprivate(set) var secrets: [Secret] = []
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ extension SmartCard {
|
|||||||
public class Store: SecretStore {
|
public class Store: SecretStore {
|
||||||
|
|
||||||
// TODO: Read actual smart card name, eg "YubiKey 5c"
|
// TODO: Read actual smart card name, eg "YubiKey 5c"
|
||||||
|
@Published public var isAvailable: Bool = false
|
||||||
public let name = NSLocalizedString("Smart Card", comment: "Smart Card")
|
public let name = NSLocalizedString("Smart Card", comment: "Smart Card")
|
||||||
@Published public fileprivate(set) var secrets: [Secret] = []
|
@Published public fileprivate(set) var secrets: [Secret] = []
|
||||||
fileprivate let watcher = TKTokenWatcher()
|
fileprivate let watcher = TKTokenWatcher()
|
||||||
@ -21,10 +22,11 @@ extension SmartCard {
|
|||||||
guard !string.contains("setoken") else { return }
|
guard !string.contains("setoken") else { return }
|
||||||
self.id = string
|
self.id = string
|
||||||
self.reloadSecrets()
|
self.reloadSecrets()
|
||||||
self.watcher.addRemovalHandler(self.reloadSecrets, forTokenID: string)
|
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
|
||||||
}
|
}
|
||||||
if let id = id {
|
if let id = id {
|
||||||
self.watcher.addRemovalHandler(self.reloadSecrets, forTokenID: id)
|
self.isAvailable = true
|
||||||
|
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: id)
|
||||||
}
|
}
|
||||||
loadSecrets()
|
loadSecrets()
|
||||||
}
|
}
|
||||||
@ -70,8 +72,14 @@ extension SmartCard {
|
|||||||
|
|
||||||
extension SmartCard.Store {
|
extension SmartCard.Store {
|
||||||
|
|
||||||
fileprivate func reloadSecrets(for tokenID: String? = nil) {
|
fileprivate func smartcardRemoved(for tokenID: String? = nil) {
|
||||||
|
id = nil
|
||||||
|
reloadSecrets()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func reloadSecrets() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
self.isAvailable = self.id != nil
|
||||||
self.secrets.removeAll()
|
self.secrets.removeAll()
|
||||||
self.loadSecrets()
|
self.loadSecrets()
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
window.makeKeyAndOrderFront(nil)
|
window.makeKeyAndOrderFront(nil)
|
||||||
window.titleVisibility = .hidden
|
window.titleVisibility = .hidden
|
||||||
window.toolbar = toolbar
|
window.toolbar = toolbar
|
||||||
|
if secureEnclave.isAvailable {
|
||||||
let plus = NSTitlebarAccessoryViewController()
|
let plus = NSTitlebarAccessoryViewController()
|
||||||
plus.view = NSButton(image: NSImage(named: NSImage.addTemplateName)!, target: self, action: #selector(add(sender:)))
|
plus.view = NSButton(image: NSImage(named: NSImage.addTemplateName)!, target: self, action: #selector(add(sender:)))
|
||||||
plus.layoutAttribute = .right
|
plus.layoutAttribute = .right
|
||||||
window.addTitlebarAccessoryViewController(plus)
|
window.addTitlebarAccessoryViewController(plus)
|
||||||
|
}
|
||||||
runSetupIfNeeded()
|
runSetupIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ struct ContentView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
List(selection: $active) {
|
List(selection: $active) {
|
||||||
|
if secureEnclave.isAvailable {
|
||||||
Section(header: Text(secureEnclave.name)) {
|
Section(header: Text(secureEnclave.name)) {
|
||||||
ForEach(secureEnclave.secrets) { secret in
|
ForEach(secureEnclave.secrets) { secret in
|
||||||
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) {
|
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) {
|
||||||
@ -24,6 +25,8 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if smartCard.isAvailable {
|
||||||
Section(header: Text(smartCard.name)) {
|
Section(header: Text(smartCard.name)) {
|
||||||
ForEach(smartCard.secrets) { secret in
|
ForEach(smartCard.secrets) { secret in
|
||||||
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) {
|
NavigationLink(destination: SecretDetailView(secret: secret), tag: secret.id, selection: self.$active) {
|
||||||
@ -31,6 +34,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.onAppear {
|
}.onAppear {
|
||||||
self.active = self.secureEnclave.secrets.first?.id ?? self.smartCard.secrets.first?.id
|
self.active = self.secureEnclave.secrets.first?.id ?? self.smartCard.secrets.first?.id
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ extension Preview {
|
|||||||
|
|
||||||
class Store: SecretStore, ObservableObject {
|
class Store: SecretStore, ObservableObject {
|
||||||
|
|
||||||
|
let isAvailable = true
|
||||||
let name = "Preview Store"
|
let name = "Preview Store"
|
||||||
@Published var secrets: [Secret] = []
|
@Published var secrets: [Secret] = []
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user