Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Keychain-backed settings #536

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Next Next commit
Add SettingsStore
  • Loading branch information
paulhdk committed Sep 16, 2024
commit 2a58323706839f2f43fd0a46c7f67ff655587326
60 changes: 60 additions & 0 deletions Sources/Secretive/Helpers/SettingsHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// SettingsHelper.swift
// Secretive
//
// Created by Paul Heidekrüger on 27.02.24.
// Copyright © 2024 Max Goedjen. All rights reserved.
//

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//
// SettingsHelper.swift
// Secretive
//
// Created by Paul Heidekrüger on 27.02.24.
// Copyright © 2024 Max Goedjen. All rights reserved.
//

import Foundation

class SettingsStore {
static let service = "com.maxgoedjen.Secretive"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

extension SettingsStore {
static func set(key: String, value: String) -> Bool {
paulhdk marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably makes sense to have the core get/set methods her expose Data directly (and maybe have a couple thin wrapper methods for string).

let valueData = value.data(using: String.Encoding.utf8)!

if let keyVal = get(key: key) {
if keyVal == value {
return true
}

let updateQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrServer as String: service]
let attributes: [String: Any] = [kSecAttrAccount as String: key,
kSecValueData as String: valueData]
// FIXME: Make this non-blocking as described here: https://developer.apple.com/documentation/security/1393617-secitemupdate
let status = SecItemUpdate(updateQuery as CFDictionary, attributes as CFDictionary)
guard status == errSecSuccess else {
print("Couldn't update item in keychain. " + status.description)
return false
}
} else {
let addquery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecAttrServer as String: service,
kSecValueData as String: valueData]
// FIXME: Make this non-blocking as described here: https://developer.apple.com/documentation/security/1401659-secitemadd
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way you'll really get around this is by caching these values. I'm not super worried about this in this context, assuming you're not seeing any noticeable chug from it.

let status = SecItemAdd(addquery as CFDictionary, nil)
guard status == errSecSuccess else {
print("Couldn't add item to keychain. " + status.description)
return false
}
}
return true
}

static func get(key: String) -> String? {
let getquery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecAttrServer as String: service,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true]
var item: CFTypeRef?
let status = SecItemCopyMatching(getquery as CFDictionary, &item)

return status == errSecSuccess ? String(decoding: item as! Data, as: UTF8.self) : nil
}
}