Add descriptions for unavailable keys (#708)

* Describe unavailable key types

* Cleanup
This commit is contained in:
Max Goedjen 2025-09-14 14:42:41 -07:00 committed by GitHub
parent 3f247d628f
commit d7f8d5e56b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 82 additions and 25 deletions

View File

@ -5833,6 +5833,17 @@
} }
} }
}, },
"create_secret_key_type_macOS_update_required_label" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Unavailable on this version of macOS"
}
}
}
},
"create_secret_mldsa_warning" : { "create_secret_mldsa_warning" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {

View File

@ -64,7 +64,7 @@ public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiab
private let _create: @Sendable (String, Attributes) async throws -> AnySecret private let _create: @Sendable (String, Attributes) async throws -> AnySecret
private let _delete: @Sendable (AnySecret) async throws -> Void private let _delete: @Sendable (AnySecret) async throws -> Void
private let _update: @Sendable (AnySecret, String, Attributes) async throws -> Void private let _update: @Sendable (AnySecret, String, Attributes) async throws -> Void
private let _supportedKeyTypes: @Sendable () -> [KeyType] private let _supportedKeyTypes: @Sendable () -> KeyAvailability
public init<SecretStoreType>(_ secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable { public init<SecretStoreType>(_ secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable {
_create = { AnySecret(try await secretStore.create(name: $0, attributes: $1)) } _create = { AnySecret(try await secretStore.create(name: $0, attributes: $1)) }
@ -87,7 +87,7 @@ public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiab
try await _update(secret, name, attributes) try await _update(secret, name, attributes)
} }
public var supportedKeyTypes: [KeyType] { public var supportedKeyTypes: KeyAvailability {
_supportedKeyTypes() _supportedKeyTypes()
} }

View File

@ -62,10 +62,37 @@ public protocol SecretStoreModifiable<SecretType>: SecretStore {
/// - attributes: The new attributes for the secret. /// - attributes: The new attributes for the secret.
func update(secret: SecretType, name: String, attributes: Attributes) async throws func update(secret: SecretType, name: String, attributes: Attributes) async throws
var supportedKeyTypes: [KeyType] { get } var supportedKeyTypes: KeyAvailability { get }
} }
public struct KeyAvailability: Sendable {
public let available: [KeyType]
public let unavailable: [UnavailableKeyType]
public init(available: [KeyType], unavailable: [UnavailableKeyType]) {
self.available = available
self.unavailable = unavailable
}
public struct UnavailableKeyType: Sendable {
public let keyType: KeyType
public let reason: Reason
public init(keyType: KeyType, reason: Reason) {
self.keyType = keyType
self.reason = reason
}
public enum Reason: Sendable {
case macOSUpdateRequired
}
}
}
extension NSNotification.Name { extension NSNotification.Name {
// Distributed notification that keys were modified out of process (ie, that the management tool added/removed secrets) // Distributed notification that keys were modified out of process (ie, that the management tool added/removed secrets)

View File

@ -186,17 +186,22 @@ extension SecureEnclave {
await reloadSecrets() await reloadSecrets()
} }
public var supportedKeyTypes: [KeyType] { public let supportedKeyTypes: KeyAvailability = {
if #available(macOS 26, *) { let macOS26Keys: [KeyType] = [.mldsa65, .mldsa87]
[ let isAtLeastMacOS26 = if #available(macOS 26, *) {
.ecdsa256, true
.mldsa65,
.mldsa87,
]
} else { } else {
[.ecdsa256] false
} }
} return KeyAvailability(
available: [
.ecdsa256,
] + (isAtLeastMacOS26 ? macOS26Keys : []),
unavailable: (isAtLeastMacOS26 ? [] : macOS26Keys).map {
KeyAvailability.UnavailableKeyType(keyType: $0, reason: .macOSUpdateRequired)
}
)
}()
} }
} }

View File

@ -60,16 +60,17 @@ extension Preview {
let id = UUID() let id = UUID()
var name: String { "Modifiable Preview Store" } var name: String { "Modifiable Preview Store" }
let secrets: [Secret] let secrets: [Secret]
var supportedKeyTypes: [KeyType] { var supportedKeyTypes: KeyAvailability {
if #available(macOS 26, *) { return KeyAvailability(
[ available: [
.ecdsa256, .ecdsa256,
.mldsa65, .mldsa65,
.mldsa87, .mldsa87
],
unavailable: [
.init(keyType: .ecdsa384, reason: .macOSUpdateRequired)
] ]
} else { )
[.ecdsa256]
}
} }
init(secrets: [Secret]) { init(secrets: [Secret]) {

View File

@ -75,10 +75,23 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
Section { Section {
VStack { VStack {
Picker(.createSecretKeyTypeLabel, selection: $keyType) { Picker(.createSecretKeyTypeLabel, selection: $keyType) {
ForEach(store.supportedKeyTypes, id: \.self) { option in ForEach(store.supportedKeyTypes.available, id: \.self) { option in
Text(String(describing: option)) Text(String(describing: option))
.tag(option) .tag(option)
.font(.caption) }
Divider()
ForEach(store.supportedKeyTypes.unavailable, id: \.keyType) { option in
VStack {
Button {
} label: {
Text(String(describing: option.keyType))
switch option.reason {
case .macOSUpdateRequired:
Text(.createSecretKeyTypeMacOSUpdateRequiredLabel)
}
}
}
.selectionDisabled()
} }
} }
if keyType?.algorithm == .mldsa { if keyType?.algorithm == .mldsa {
@ -119,7 +132,7 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
.padding() .padding()
} }
.onAppear { .onAppear {
keyType = store.supportedKeyTypes.first keyType = store.supportedKeyTypes.available.first
} }
.formStyle(.grouped) .formStyle(.grouped)
} }
@ -146,6 +159,6 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
} }
//#Preview { #Preview {
// CreateSecretView(store: Preview.StoreModifiable()) { _ in } CreateSecretView(store: Preview.StoreModifiable()) { _ in }
//} }