mirror of
https://codeberg.org/secana/Forji.git
synced 2026-06-16 05:13:55 -07:00
fix: configure Catalyst keychain access
This commit is contained in:
parent
e1feeb865c
commit
577f1ae262
3 changed files with 55 additions and 16 deletions
|
|
@ -437,6 +437,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Forji/Forji.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.5;
|
||||
DEVELOPMENT_TEAM = RVT2M7QTD4;
|
||||
|
|
@ -475,6 +476,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = Forji/Forji.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1.5;
|
||||
DEVELOPMENT_TEAM = RVT2M7QTD4;
|
||||
|
|
|
|||
10
Forji/Forji/Forji.entitlements
Normal file
10
Forji/Forji/Forji.entitlements
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -12,6 +12,12 @@ actor KeychainManager {
|
|||
"\(server)_\(username)\(suffix)"
|
||||
}
|
||||
|
||||
private nonisolated static func dataProtectionQuery(_ attributes: [String: Any]) -> [String: Any] {
|
||||
var query = attributes
|
||||
query[kSecUseDataProtectionKeychain as String] = true
|
||||
return query
|
||||
}
|
||||
|
||||
// MARK: - Password
|
||||
|
||||
func savePassword(_ password: String, for server: String, username: String) throws {
|
||||
|
|
@ -53,13 +59,13 @@ actor KeychainManager {
|
|||
|
||||
nonisolated static func getTokenSync(for server: String, username: String) -> String? {
|
||||
let account = "\(server)_\(username)_token"
|
||||
let query: [String: Any] = [
|
||||
let query = dataProtectionQuery([
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: serviceName,
|
||||
kSecAttrAccount as String: account,
|
||||
kSecReturnData as String: true,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
]
|
||||
])
|
||||
var result: AnyObject?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
||||
guard status == errSecSuccess,
|
||||
|
|
@ -85,40 +91,40 @@ actor KeychainManager {
|
|||
|
||||
private func saveItem(_ value: String, forKey key: String) throws {
|
||||
guard let data = value.data(using: .utf8) else {
|
||||
throw KeychainError.unableToSave
|
||||
throw KeychainError.unableToEncode
|
||||
}
|
||||
|
||||
let deleteQuery: [String: Any] = [
|
||||
let deleteQuery = Self.dataProtectionQuery([
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: Self.serviceName,
|
||||
kSecAttrAccount as String: key,
|
||||
]
|
||||
])
|
||||
|
||||
SecItemDelete(deleteQuery as CFDictionary)
|
||||
|
||||
let addQuery: [String: Any] = [
|
||||
let addQuery = Self.dataProtectionQuery([
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: Self.serviceName,
|
||||
kSecAttrAccount as String: key,
|
||||
kSecValueData as String: data,
|
||||
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
|
||||
]
|
||||
])
|
||||
|
||||
let status = SecItemAdd(addQuery as CFDictionary, nil)
|
||||
|
||||
guard status == errSecSuccess else {
|
||||
throw KeychainError.unableToSave
|
||||
throw KeychainError.unableToSave(status)
|
||||
}
|
||||
}
|
||||
|
||||
private func getItem(forKey key: String) throws -> String {
|
||||
let query: [String: Any] = [
|
||||
let query = Self.dataProtectionQuery([
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: Self.serviceName,
|
||||
kSecAttrAccount as String: key,
|
||||
kSecReturnData as String: true,
|
||||
kSecMatchLimit as String: kSecMatchLimitOne,
|
||||
]
|
||||
])
|
||||
|
||||
var result: AnyObject?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
||||
|
|
@ -134,22 +140,43 @@ actor KeychainManager {
|
|||
}
|
||||
|
||||
private func deleteItem(forKey key: String) throws {
|
||||
let query: [String: Any] = [
|
||||
let query = Self.dataProtectionQuery([
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: Self.serviceName,
|
||||
kSecAttrAccount as String: key,
|
||||
]
|
||||
])
|
||||
|
||||
let status = SecItemDelete(query as CFDictionary)
|
||||
|
||||
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||||
throw KeychainError.unableToDelete
|
||||
throw KeychainError.unableToDelete(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum KeychainError: Error {
|
||||
case unableToSave
|
||||
enum KeychainError: LocalizedError {
|
||||
case unableToEncode
|
||||
case unableToSave(OSStatus)
|
||||
case notFound
|
||||
case unableToDelete
|
||||
case unableToDelete(OSStatus)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .unableToEncode:
|
||||
return "Unable to encode keychain value."
|
||||
case let .unableToSave(status):
|
||||
return "Unable to save keychain item: \(Self.describe(status))."
|
||||
case .notFound:
|
||||
return "Keychain item not found."
|
||||
case let .unableToDelete(status):
|
||||
return "Unable to delete keychain item: \(Self.describe(status))."
|
||||
}
|
||||
}
|
||||
|
||||
private static func describe(_ status: OSStatus) -> String {
|
||||
if let message = SecCopyErrorMessageString(status, nil) as String? {
|
||||
return "\(message) (OSStatus \(status))"
|
||||
}
|
||||
return "OSStatus \(status)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue