Merge pull request #191 from writefreely/throw-keychain-errors-instead-of-crashing

Throw (and handle) Keychain error instead of crashing
This commit is contained in:
Angelo Stavrow 2021-08-30 09:19:55 -04:00 committed by GitHub
commit b523606f6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 23 deletions

View File

@ -6,6 +6,9 @@ enum AccountError: Error {
case usernameNotFound
case serverNotFound
case invalidServerURL
case couldNotSaveTokenToKeychain
case couldNotFetchTokenFromKeychain
case couldNotDeleteTokenFromKeychain
}
extension AccountError: LocalizedError {
@ -31,6 +34,21 @@ extension AccountError: LocalizedError {
"Please enter a valid instance domain name. It should look like \"https://example.com\" or \"write.as\".", // swiftlint:disable:this line_length
comment: ""
)
case .couldNotSaveTokenToKeychain:
return NSLocalizedString(
"There was a problem trying to save your access token to the device, please try logging in again.",
comment: ""
)
case .couldNotFetchTokenFromKeychain:
return NSLocalizedString(
"There was a problem trying to fetch your access token from the device, please try logging in again.",
comment: ""
)
case .couldNotDeleteTokenFromKeychain:
return NSLocalizedString(
"There was a problem trying to delete your access token from the device, please try logging out again.",
comment: ""
)
}
}
}

View File

@ -10,9 +10,16 @@ extension WriteFreelyModel {
let user = try result.get()
fetchUserCollections()
fetchUserPosts()
saveTokenToKeychain(user.token, username: user.username, server: account.server)
DispatchQueue.main.async {
self.account.login(user)
do {
try saveTokenToKeychain(user.token, username: user.username, server: account.server)
DispatchQueue.main.async {
self.account.login(user)
}
} catch {
DispatchQueue.main.async {
self.loginErrorMessage = "There was a problem storing your access token to the Keychain."
self.isPresentingLoginErrorAlert = true
}
}
} catch WFError.notFound {
DispatchQueue.main.async {

View File

@ -1,7 +1,14 @@
import Foundation
extension WriteFreelyModel {
func saveTokenToKeychain(_ token: String, username: String?, server: String) {
enum WFKeychainError: Error {
case saveToKeychainFailed
case purgeFromKeychainFailed
case fetchFromKeychainFailed
}
func saveTokenToKeychain(_ token: String, username: String?, server: String) throws {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecValueData as String: token.data(using: .utf8)!,
@ -10,7 +17,7 @@ extension WriteFreelyModel {
]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecDuplicateItem || status == errSecSuccess else {
fatalError("Error storing in Keychain with OSStatus: \(status)")
throw WFKeychainError.saveToKeychainFailed
}
}
@ -22,11 +29,11 @@ extension WriteFreelyModel {
]
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else {
fatalError("Error deleting from Keychain with OSStatus: \(status)")
throw WFKeychainError.purgeFromKeychainFailed
}
}
func fetchTokenFromKeychain(username: String?, server: String) -> String? {
func fetchTokenFromKeychain(username: String?, server: String) throws -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: username ?? "anonymous",
@ -41,7 +48,7 @@ extension WriteFreelyModel {
return nil
}
guard status == errSecSuccess else {
fatalError("Error fetching from Keychain with OSStatus: \(status)")
throw WFKeychainError.fetchFromKeychainFailed
}
guard let existingSecItem = secItem as? [String: Any],
let tokenData = existingSecItem[kSecValueData as String] as? Data,
@ -50,4 +57,5 @@ extension WriteFreelyModel {
}
return token
}
}

View File

@ -51,18 +51,25 @@ final class WriteFreelyModel: ObservableObject {
print("Server URL not found")
return
}
guard let token = self.fetchTokenFromKeychain(
username: self.account.username,
server: self.account.server
) else {
print("Could not fetch token from Keychain")
return
do {
guard let token = try self.fetchTokenFromKeychain(
username: self.account.username,
server: self.account.server
) else {
self.loginErrorMessage = AccountError.couldNotFetchTokenFromKeychain.localizedDescription
self.isPresentingLoginErrorAlert = true
return
}
self.account.login(WFUser(token: token, username: self.account.username))
self.client = WFClient(for: serverURL)
self.client?.user = self.account.user
self.fetchUserCollections()
self.fetchUserPosts()
} catch {
self.loginErrorMessage = AccountError.couldNotFetchTokenFromKeychain.localizedDescription
self.isPresentingLoginErrorAlert = true
}
self.account.login(WFUser(token: token, username: self.account.username))
self.client = WFClient(for: serverURL)
self.client?.user = self.account.user
self.fetchUserCollections()
self.fetchUserPosts()
}
}

View File

@ -969,7 +969,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 612;
CURRENT_PROJECT_VERSION = 615;
DEVELOPMENT_TEAM = TPPAB4YBA6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
@ -978,7 +978,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.6;
MARKETING_VERSION = 1.0.7;
PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform";
PRODUCT_NAME = "WriteFreely-MultiPlatform";
SDKROOT = iphoneos;
@ -993,7 +993,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 612;
CURRENT_PROJECT_VERSION = 615;
DEVELOPMENT_TEAM = TPPAB4YBA6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
@ -1002,7 +1002,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.6;
MARKETING_VERSION = 1.0.7;
PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform";
PRODUCT_NAME = "WriteFreely-MultiPlatform";
SDKROOT = iphoneos;