mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-09-01 10:00:56 +00:00
WIP
This commit is contained in:
parent
11c92327ac
commit
bff58b00ec
@ -2179,6 +2179,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Current Biometrics" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"delete_confirmation_cancel_button" : {
|
"delete_confirmation_cancel_button" : {
|
||||||
"extractionState" : "manual",
|
"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" : {
|
"Key Attribution" : {
|
||||||
|
|
||||||
@ -3555,6 +3561,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Require authentication with current set of biometrics." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"secret_detail_md5_fingerprint_label" : {
|
"secret_detail_md5_fingerprint_label" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
@ -5350,6 +5359,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Test" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"test@example.com" : {
|
"test@example.com" : {
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ public struct Attributes: Sendable, Codable, Hashable {
|
|||||||
authentication: AuthenticationRequirement = .presenceRequired,
|
authentication: AuthenticationRequirement = .presenceRequired,
|
||||||
publicKeyAttribution: String? = nil
|
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.keyType = keyType
|
||||||
self.authentication = authentication
|
self.authentication = authentication
|
||||||
self.publicKeyAttribution = publicKeyAttribution
|
self.publicKeyAttribution = publicKeyAttribution
|
||||||
|
@ -13,6 +13,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
@MainActor private let storeList: SecretStoreList = {
|
@MainActor private let storeList: SecretStoreList = {
|
||||||
let list = SecretStoreList()
|
let list = SecretStoreList()
|
||||||
|
// list.add(store: SecureEnclave.CryptoKitStore())
|
||||||
list.add(store: SecureEnclave.Store())
|
list.add(store: SecureEnclave.Store())
|
||||||
list.add(store: SmartCard.Store())
|
list.add(store: SmartCard.Store())
|
||||||
return list
|
return list
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import Cocoa
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SecretKit
|
import SecretKit
|
||||||
import SecureEnclaveSecretKit
|
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).
|
// 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 = {
|
@MainActor fileprivate static let _secretStoreList: SecretStoreList = {
|
||||||
let list = SecretStoreList()
|
let list = SecretStoreList()
|
||||||
|
list.add(store: SecureEnclave.Store())
|
||||||
list.add(store: SecureEnclave.CryptoKitStore())
|
list.add(store: SecureEnclave.CryptoKitStore())
|
||||||
list.add(store: SmartCard.Store())
|
list.add(store: SmartCard.Store())
|
||||||
return list
|
return list
|
||||||
|
@ -30,7 +30,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.frame(minWidth: 640, minHeight: 320)
|
.frame(minWidth: 640, minHeight: 320)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
// toolbarItem(updateNoticeView, id: "update")
|
toolbarItem(updateNoticeView, id: "update")
|
||||||
toolbarItem(runningOrRunSetupView, id: "setup")
|
toolbarItem(runningOrRunSetupView, id: "setup")
|
||||||
toolbarItem(appPathNoticeView, id: "appPath")
|
toolbarItem(appPathNoticeView, id: "appPath")
|
||||||
toolbarItem(newItemView, id: "new")
|
toolbarItem(newItemView, id: "new")
|
||||||
|
@ -8,7 +8,9 @@ struct CopyableView: View {
|
|||||||
var text: String
|
var text: String
|
||||||
|
|
||||||
@State private var interactionState: InteractionState = .normal
|
@State private var interactionState: InteractionState = .normal
|
||||||
|
@State private var expanded = false
|
||||||
|
@State private var showExpand = false
|
||||||
|
|
||||||
var content: some View {
|
var content: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack {
|
HStack {
|
||||||
@ -31,17 +33,50 @@ struct CopyableView: View {
|
|||||||
}
|
}
|
||||||
.padding(EdgeInsets(top: 20, leading: 20, bottom: 10, trailing: 20))
|
.padding(EdgeInsets(top: 20, leading: 20, bottom: 10, trailing: 20))
|
||||||
Divider()
|
Divider()
|
||||||
Text(text)
|
textView
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.lineLimit(expanded ? nil : 5)
|
||||||
.foregroundColor(primaryTextColor)
|
.overlay {
|
||||||
.padding(EdgeInsets(top: 10, leading: 20, bottom: 20, trailing: 20))
|
ViewThatFits {
|
||||||
.multilineTextAlignment(.leading)
|
textView
|
||||||
.font(.system(.body, design: .monospaced))
|
.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)
|
._background(interactionState: interactionState)
|
||||||
.frame(minWidth: 150, maxWidth: .infinity)
|
.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 {
|
var body: some View {
|
||||||
content
|
content
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
@ -49,10 +84,10 @@ struct CopyableView: View {
|
|||||||
interactionState = hovering ? .hovering : .normal
|
interactionState = hovering ? .hovering : .normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDrag({
|
.draggable(text, preview: {
|
||||||
NSItemProvider(item: NSData(data: text.data(using: .utf8)!), typeIdentifier: UTType.utf8PlainText.identifier)
|
content
|
||||||
}, preview: {
|
.lineLimit(3)
|
||||||
content
|
.frame(maxWidth: 300)
|
||||||
._background(interactionState: .dragging)
|
._background(interactionState: .dragging)
|
||||||
})
|
})
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
@ -170,7 +205,7 @@ struct CopyableView_Previews: PreviewProvider {
|
|||||||
Group {
|
Group {
|
||||||
CopyableView(title: "secret_detail_sha256_fingerprint_label", image: Image(systemName: "figure.wave"), text: "Hello world.")
|
CopyableView(title: "secret_detail_sha256_fingerprint_label", image: Image(systemName: "figure.wave"), text: "Hello world.")
|
||||||
.padding()
|
.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()
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,11 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
|||||||
@State var advanced = false
|
@State var advanced = false
|
||||||
|
|
||||||
private var authenticationOptions: [AuthenticationRequirement] {
|
private var authenticationOptions: [AuthenticationRequirement] {
|
||||||
[.presenceRequired, .notRequired]
|
if advanced || authenticationRequirement == .biometryCurrent {
|
||||||
|
[.presenceRequired, .notRequired, .biometryCurrent]
|
||||||
|
} else {
|
||||||
|
[.presenceRequired, .notRequired]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -21,11 +25,48 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
|||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
TextField(String(localized: .createSecretNameLabel), text: $name, prompt: Text(.createSecretNamePlaceholder))
|
TextField(String(localized: .createSecretNameLabel), text: $name, prompt: Text(.createSecretNamePlaceholder))
|
||||||
Picker(.createSecretRequireAuthenticationTitle, selection: $authenticationRequirement) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
ForEach(authenticationOptions) { option in
|
Picker(.createSecretRequireAuthenticationTitle, selection: $authenticationRequirement) {
|
||||||
Text(String(describing: option))
|
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)
|
.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 {
|
if advanced {
|
||||||
@ -48,7 +89,8 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
|||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
TextField("Key Attribution", text: $keyAttribution, prompt: Text("test@example.com"))
|
TextField("Key Attribution", text: $keyAttribution, prompt: Text("test@example.com"))
|
||||||
Text("This shows at the end of your public key.")
|
Text("This shows at the end of your public key.")
|
||||||
.font(.caption)
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
|
|||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
TextField("Key Attribution", text: $publicKeyAttribution, prompt: Text("test@example.com"))
|
TextField("Key Attribution", text: $publicKeyAttribution, prompt: Text("test@example.com"))
|
||||||
Text("This shows at the end of your public key.")
|
Text("This shows at the end of your public key.")
|
||||||
.font(.caption)
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ struct ToolbarButtonStyle: ButtonStyle {
|
|||||||
configuration
|
configuration
|
||||||
.label
|
.label
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(EdgeInsets(top: 6, leading: 8, bottom: 6, trailing: 8))
|
.padding(.vertical, 6)
|
||||||
|
.padding(.horizontal, 9)
|
||||||
.glassEffect(.regular.tint(glassTint), in: .capsule)
|
.glassEffect(.regular.tint(glassTint), in: .capsule)
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
self.hovering = hovering
|
self.hovering = hovering
|
||||||
@ -41,6 +42,8 @@ struct ToolbarButtonStyle: ButtonStyle {
|
|||||||
.label
|
.label
|
||||||
.background(colorScheme == .light ? lightColor : darkColor)
|
.background(colorScheme == .light ? lightColor : darkColor)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
.padding(.vertical, 6)
|
||||||
|
.padding(.horizontal, 8)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 5))
|
.clipShape(RoundedRectangle(cornerRadius: 5))
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 5)
|
RoundedRectangle(cornerRadius: 5)
|
||||||
|
Loading…
Reference in New Issue
Block a user