This commit is contained in:
Max Goedjen 2025-08-23 12:51:38 -07:00
parent 11c92327ac
commit bff58b00ec
No known key found for this signature in database
9 changed files with 116 additions and 22 deletions

View File

@ -2179,6 +2179,9 @@
}
}
}
},
"Current Biometrics" : {
},
"delete_confirmation_cancel_button" : {
"extractionState" : "manual",
@ -2949,6 +2952,9 @@
}
}
}
},
"If you change your biometric settings in _any way_, including adding a new fingerprint, this key will no longer be accessible." : {
},
"Key Attribution" : {
@ -3555,6 +3561,9 @@
}
}
}
},
"Require authentication with current set of biometrics." : {
},
"secret_detail_md5_fingerprint_label" : {
"extractionState" : "manual",
@ -5350,6 +5359,9 @@
}
}
}
},
"Test" : {
},
"test@example.com" : {

View File

@ -17,7 +17,7 @@ public struct Attributes: Sendable, Codable, Hashable {
authentication: AuthenticationRequirement = .presenceRequired,
publicKeyAttribution: String? = nil
) {
assert(authentication != .unknown, "Secrets cannot be created with an unknown authentication requirement.")
// assert(authentication != .unknown, "Secrets cannot be created with an unknown authentication requirement.")
self.keyType = keyType
self.authentication = authentication
self.publicKeyAttribution = publicKeyAttribution

View File

@ -13,6 +13,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@MainActor private let storeList: SecretStoreList = {
let list = SecretStoreList()
// list.add(store: SecureEnclave.CryptoKitStore())
list.add(store: SecureEnclave.Store())
list.add(store: SmartCard.Store())
return list

View File

@ -1,4 +1,3 @@
import Cocoa
import SwiftUI
import SecretKit
import SecureEnclaveSecretKit
@ -10,6 +9,7 @@ extension EnvironmentValues {
// This is injected through .environment modifier below instead of @Entry for performance reasons (basially, restrictions around init/mainactor causing delay in loading secrets/"empty screen" blip).
@MainActor fileprivate static let _secretStoreList: SecretStoreList = {
let list = SecretStoreList()
list.add(store: SecureEnclave.Store())
list.add(store: SecureEnclave.CryptoKitStore())
list.add(store: SmartCard.Store())
return list

View File

@ -30,7 +30,7 @@ struct ContentView: View {
}
.frame(minWidth: 640, minHeight: 320)
.toolbar {
// toolbarItem(updateNoticeView, id: "update")
toolbarItem(updateNoticeView, id: "update")
toolbarItem(runningOrRunSetupView, id: "setup")
toolbarItem(appPathNoticeView, id: "appPath")
toolbarItem(newItemView, id: "new")

View File

@ -8,7 +8,9 @@ struct CopyableView: View {
var text: String
@State private var interactionState: InteractionState = .normal
@State private var expanded = false
@State private var showExpand = false
var content: some View {
VStack(alignment: .leading) {
HStack {
@ -31,17 +33,50 @@ struct CopyableView: View {
}
.padding(EdgeInsets(top: 20, leading: 20, bottom: 10, trailing: 20))
Divider()
Text(text)
.fixedSize(horizontal: false, vertical: true)
.foregroundColor(primaryTextColor)
.padding(EdgeInsets(top: 10, leading: 20, bottom: 20, trailing: 20))
.multilineTextAlignment(.leading)
.font(.system(.body, design: .monospaced))
textView
.lineLimit(expanded ? nil : 5)
.overlay {
ViewThatFits {
textView
.hidden()
.onAppear {
showExpand = false
}
Spacer()
.onAppear {
showExpand = true
}
}
}
.overlay(alignment: .bottomTrailing) {
if showExpand {
if #available(macOS 26.0, *) {
Button("Test", systemImage: "rectangle.expand.vertical") {
expanded = true
showExpand = false
}
.labelStyle(.iconOnly)
.buttonBorderShape(.circle)
.buttonStyle(.glass)
.padding()
} else {
}
}
}
}
._background(interactionState: interactionState)
.frame(minWidth: 150, maxWidth: .infinity)
}
var textView: some View {
Text(text)
.fixedSize(horizontal: false, vertical: true)
.foregroundColor(primaryTextColor)
.padding(EdgeInsets(top: 10, leading: 20, bottom: 20, trailing: 20))
.multilineTextAlignment(.leading)
.font(.system(.body, design: .monospaced))
}
var body: some View {
content
.onHover { hovering in
@ -49,10 +84,10 @@ struct CopyableView: View {
interactionState = hovering ? .hovering : .normal
}
}
.onDrag({
NSItemProvider(item: NSData(data: text.data(using: .utf8)!), typeIdentifier: UTType.utf8PlainText.identifier)
}, preview: {
content
.draggable(text, preview: {
content
.lineLimit(3)
.frame(maxWidth: 300)
._background(interactionState: .dragging)
})
.onTapGesture {
@ -170,7 +205,7 @@ struct CopyableView_Previews: PreviewProvider {
Group {
CopyableView(title: "secret_detail_sha256_fingerprint_label", image: Image(systemName: "figure.wave"), text: "Hello world.")
.padding()
CopyableView(title: "secret_detail_sha256_fingerprint_label", image: Image(systemName: "figure.wave"), text: "Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. ")
CopyableView(title: "secret_detail_sha256_fingerprint_label", image: Image(systemName: "figure.wave"), text: "Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text.")
.padding()
}
}

View File

@ -13,7 +13,11 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
@State var advanced = false
private var authenticationOptions: [AuthenticationRequirement] {
[.presenceRequired, .notRequired]
if advanced || authenticationRequirement == .biometryCurrent {
[.presenceRequired, .notRequired, .biometryCurrent]
} else {
[.presenceRequired, .notRequired]
}
}
var body: some View {
@ -21,11 +25,48 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
Form {
Section {
TextField(String(localized: .createSecretNameLabel), text: $name, prompt: Text(.createSecretNamePlaceholder))
Picker(.createSecretRequireAuthenticationTitle, selection: $authenticationRequirement) {
ForEach(authenticationOptions) { option in
Text(String(describing: option))
VStack(alignment: .leading, spacing: 10) {
Picker(.createSecretRequireAuthenticationTitle, selection: $authenticationRequirement) {
ForEach(authenticationOptions) { option in
HStack {
switch option {
case .notRequired:
Image(systemName: "bell")
Text(.createSecretNotifyTitle)
case .presenceRequired:
Image(systemName: "lock")
Text(.createSecretRequireAuthenticationTitle)
case .biometryCurrent:
Image(systemName: "lock.trianglebadge.exclamationmark.fill")
Text("Current Biometrics")
case .unknown:
EmptyView()
}
}
.tag(option)
}
}
Group {
switch authenticationRequirement {
case .notRequired:
Text(.createSecretNotifyDescription)
case .presenceRequired:
Text(.createSecretRequireAuthenticationDescription)
case .biometryCurrent:
Text("Require authentication with current set of biometrics.")
case .unknown:
EmptyView()
}
}
.font(.subheadline)
.foregroundStyle(.secondary)
if authenticationRequirement == .biometryCurrent {
Text("If you change your biometric settings in _any way_, including adding a new fingerprint, this key will no longer be accessible.")
.padding(.horizontal, 10)
.padding(.vertical, 3)
.background(.red.opacity(0.5), in: RoundedRectangle(cornerRadius: 5))
}
}
}
if advanced {
@ -48,7 +89,8 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
VStack(alignment: .leading) {
TextField("Key Attribution", text: $keyAttribution, prompt: Text("test@example.com"))
Text("This shows at the end of your public key.")
.font(.caption)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
}

View File

@ -26,7 +26,8 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
VStack(alignment: .leading) {
TextField("Key Attribution", text: $publicKeyAttribution, prompt: Text("test@example.com"))
Text("This shows at the end of your public key.")
.font(.caption)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
}

View File

@ -31,7 +31,8 @@ struct ToolbarButtonStyle: ButtonStyle {
configuration
.label
.foregroundColor(.white)
.padding(EdgeInsets(top: 6, leading: 8, bottom: 6, trailing: 8))
.padding(.vertical, 6)
.padding(.horizontal, 9)
.glassEffect(.regular.tint(glassTint), in: .capsule)
.onHover { hovering in
self.hovering = hovering
@ -41,6 +42,8 @@ struct ToolbarButtonStyle: ButtonStyle {
.label
.background(colorScheme == .light ? lightColor : darkColor)
.foregroundColor(.white)
.padding(.vertical, 6)
.padding(.horizontal, 8)
.clipShape(RoundedRectangle(cornerRadius: 5))
.overlay(
RoundedRectangle(cornerRadius: 5)