mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-26 23:20:57 +00:00
Save text (#632)
This commit is contained in:
parent
828c61cb2f
commit
f0a6f2e43b
@ -2557,67 +2557,67 @@
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Canvia el nom"
|
||||
}
|
||||
},
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Umbenennen"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Rename"
|
||||
"value" : "Save"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Renommer"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Rinomina"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "名前の変更"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "이름 변경"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Zmień nazwę"
|
||||
}
|
||||
},
|
||||
"pt-BR" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Renomear"
|
||||
}
|
||||
},
|
||||
"ru" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "Переименовать"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"state" : "needs_review",
|
||||
"value" : "重命名"
|
||||
}
|
||||
}
|
||||
|
@ -46,16 +46,16 @@ extension SecureEnclave {
|
||||
let auth: AuthenticationRequirement = String(describing: accessControl)
|
||||
.contains("DeviceOwnerAuthentication") ? .presenceRequired : .unknown
|
||||
let parsed = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: tokenObjectID)
|
||||
let secret = Secret(id: id, name: name, publicKey: parsed.publicKey.x963Representation, attributes: Attributes(keyType: .init(algorithm: .ecdsa, size: 256), authentication: auth))
|
||||
let secret = Secret(id: UUID().uuidString, name: name, publicKey: parsed.publicKey.x963Representation, attributes: Attributes(keyType: .init(algorithm: .ecdsa, size: 256), authentication: auth))
|
||||
guard !migratedPublicKeys.contains(parsed.publicKey.x963Representation) else {
|
||||
logger.log("Skipping \(name), public key already present. Marking as migrated.")
|
||||
try markMigrated(secret: secret)
|
||||
try markMigrated(secret: secret, oldID: id)
|
||||
continue
|
||||
}
|
||||
logger.log("Migrating \(name).")
|
||||
try store.saveKey(tokenObjectID, name: name, attributes: secret.attributes)
|
||||
logger.log("Migrated \(name).")
|
||||
try markMigrated(secret: secret)
|
||||
try markMigrated(secret: secret, oldID: id)
|
||||
migrated = true
|
||||
}
|
||||
if migrated {
|
||||
@ -65,13 +65,13 @@ extension SecureEnclave {
|
||||
|
||||
|
||||
|
||||
public func markMigrated(secret: Secret) throws {
|
||||
public func markMigrated(secret: Secret, oldID: Data) throws {
|
||||
let updateQuery = KeychainDictionary([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrApplicationLabel: secret.id as CFData
|
||||
kSecAttrApplicationLabel: secret.id
|
||||
])
|
||||
|
||||
let newID = secret.id + Constants.migrationMagicNumber
|
||||
let newID = oldID + Constants.migrationMagicNumber
|
||||
let updatedAttributes = KeychainDictionary([
|
||||
kSecAttrApplicationLabel: newID as CFData
|
||||
])
|
||||
|
@ -6,13 +6,13 @@ extension SecureEnclave {
|
||||
/// An implementation of Secret backed by the Secure Enclave.
|
||||
public struct Secret: SecretKit.Secret {
|
||||
|
||||
public let id: Data
|
||||
public let id: String
|
||||
public let name: String
|
||||
public let publicKey: Data
|
||||
public let attributes: Attributes
|
||||
|
||||
init(
|
||||
id: Data,
|
||||
id: String,
|
||||
name: String,
|
||||
publicKey: Data,
|
||||
attributes: Attributes
|
||||
|
@ -48,9 +48,9 @@ extension SecureEnclave {
|
||||
kSecClass: Constants.keyClass,
|
||||
kSecAttrService: Constants.keyTag,
|
||||
kSecUseDataProtectionKeychain: true,
|
||||
kSecAttrAccount: String(decoding: secret.id, as: UTF8.self),
|
||||
kSecAttrAccount: secret.id,
|
||||
kSecReturnAttributes: true,
|
||||
kSecReturnData: true
|
||||
kSecReturnData: true,
|
||||
])
|
||||
var untyped: CFTypeRef?
|
||||
let status = SecItemCopyMatching(queryAttributes, &untyped)
|
||||
@ -143,8 +143,7 @@ extension SecureEnclave {
|
||||
kSecClass: Constants.keyClass,
|
||||
kSecAttrService: Constants.keyTag,
|
||||
kSecUseDataProtectionKeychain: true,
|
||||
kSecAttrAccount: String(decoding: secret.id, as: UTF8.self),
|
||||
kSecAttrCanSign: true,
|
||||
kSecAttrAccount: secret.id,
|
||||
])
|
||||
let status = SecItemDelete(deleteAttributes)
|
||||
if status != errSecSuccess {
|
||||
@ -155,12 +154,14 @@ extension SecureEnclave {
|
||||
|
||||
public func update(secret: Secret, name: String, attributes: Attributes) async throws {
|
||||
let updateQuery = KeychainDictionary([
|
||||
kSecClass: kSecClassKey,
|
||||
kSecAttrApplicationLabel: secret.id as CFData
|
||||
kSecClass: Constants.keyClass,
|
||||
kSecAttrAccount: secret.id,
|
||||
])
|
||||
|
||||
let attributes = try JSONEncoder().encode(attributes)
|
||||
let updatedAttributes = KeychainDictionary([
|
||||
kSecAttrLabel: name,
|
||||
kSecAttrGeneric: attributes,
|
||||
])
|
||||
|
||||
let status = SecItemUpdate(updateQuery, updatedAttributes)
|
||||
@ -213,10 +214,9 @@ extension SecureEnclave.Store {
|
||||
do {
|
||||
let name = $0[kSecAttrLabel] as? String ?? String(localized: "unnamed_secret")
|
||||
guard let attributesData = $0[kSecAttrGeneric] as? Data,
|
||||
let idString = $0[kSecAttrAccount] as? String else {
|
||||
let id = $0[kSecAttrAccount] as? String else {
|
||||
throw MissingAttributesError()
|
||||
}
|
||||
let id = Data(idString.utf8)
|
||||
let attributes = try JSONDecoder().decode(Attributes.self, from: attributesData)
|
||||
let keyData = $0[kSecValueData] as! Data
|
||||
let publicKey: Data
|
||||
|
@ -8,6 +8,7 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
var dismissalBlock: (Bool) -> ()
|
||||
|
||||
@State private var confirm = ""
|
||||
@State var errorText: String?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
@ -31,6 +32,11 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let errorText {
|
||||
Text(verbatim: errorText)
|
||||
.foregroundStyle(.red)
|
||||
.font(.callout)
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Button(.deleteConfirmationDeleteButton, action: delete)
|
||||
@ -50,8 +56,12 @@ struct DeleteSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
|
||||
func delete() {
|
||||
Task {
|
||||
try! await store.delete(secret: secret)
|
||||
do {
|
||||
try await store.delete(secret: secret)
|
||||
dismissalBlock(true)
|
||||
} catch {
|
||||
errorText = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
|
||||
@State private var name: String
|
||||
@State private var publicKeyAttribution: String
|
||||
@State var errorText: String?
|
||||
|
||||
init(store: StoreType, secret: StoreType.SecretType, dismissalBlock: @escaping (Bool) -> ()) {
|
||||
self.store = store
|
||||
@ -30,6 +31,11 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
if let errorText {
|
||||
Text(verbatim: errorText)
|
||||
.foregroundStyle(.red)
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Button(.editSaveButton, action: rename)
|
||||
@ -37,7 +43,8 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
.keyboardShortcut(.return)
|
||||
Button(.editCancelButton) {
|
||||
dismissalBlock(false)
|
||||
}.keyboardShortcut(.cancelAction)
|
||||
}
|
||||
.keyboardShortcut(.cancelAction)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
@ -50,8 +57,12 @@ struct EditSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
attributes.publicKeyAttribution = publicKeyAttribution
|
||||
}
|
||||
Task {
|
||||
try? await store.update(secret: secret, name: name, attributes: attributes)
|
||||
do {
|
||||
try await store.update(secret: secret, name: name, attributes: attributes)
|
||||
dismissalBlock(true)
|
||||
} catch {
|
||||
errorText = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user