162 lines
5.8 KiB
Swift
162 lines
5.8 KiB
Swift
|
//
|
||
|
// CredentialsManager.swift
|
||
|
// NetNewsWire
|
||
|
//
|
||
|
// Created by Maurice Parker on 5/5/19.
|
||
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||
|
//
|
||
|
|
||
|
import Foundation
|
||
|
|
||
|
public struct CredentialsManager {
|
||
|
|
||
|
public static func storeCredentials(_ credentials: Credentials, server: String) throws {
|
||
|
|
||
|
switch credentials {
|
||
|
case .basic(let username, let password):
|
||
|
try storeBasicCredentials(server: server, username: username, password: password)
|
||
|
case .readerAPIBasicLogin(let username, let password):
|
||
|
try storeBasicCredentials(server: server, username: username, password: password)
|
||
|
case .readerAPIAuthLogin(let username, let apiKey):
|
||
|
try storeBasicCredentials(server: server, username: username, password: apiKey)
|
||
|
case .oauthAccessToken(let username, let token):
|
||
|
try storeBasicCredentials(server: server, username: username, password: token)
|
||
|
case .oauthRefreshToken(let username, let token):
|
||
|
try storeBasicCredentials(server: server, username: username, password: token)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public static func retrieveBasicCredentials(server: String, username: String) throws -> Credentials? {
|
||
|
|
||
|
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||
|
kSecAttrAccount as String: username,
|
||
|
kSecAttrServer as String: server,
|
||
|
kSecMatchLimit as String: kSecMatchLimitOne,
|
||
|
kSecReturnAttributes as String: true,
|
||
|
kSecReturnData as String: true]
|
||
|
|
||
|
var item: CFTypeRef?
|
||
|
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||
|
|
||
|
guard status != errSecItemNotFound else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
guard status == errSecSuccess else {
|
||
|
throw CredentialsError.unhandledError(status: status)
|
||
|
}
|
||
|
|
||
|
guard let existingItem = item as? [String : Any],
|
||
|
let passwordData = existingItem[kSecValueData as String] as? Data,
|
||
|
let password = String(data: passwordData, encoding: String.Encoding.utf8) else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return Credentials.basic(username: username, password: password)
|
||
|
|
||
|
}
|
||
|
|
||
|
public static func removeBasicCredentials(server: String, username: String) throws {
|
||
|
|
||
|
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||
|
kSecAttrAccount as String: username,
|
||
|
kSecAttrServer as String: server,
|
||
|
kSecMatchLimit as String: kSecMatchLimitOne,
|
||
|
kSecReturnAttributes as String: true,
|
||
|
kSecReturnData as String: true]
|
||
|
|
||
|
let status = SecItemDelete(query as CFDictionary)
|
||
|
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||
|
throw CredentialsError.unhandledError(status: status)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public static func retrieveReaderAPIAuthCredentials(server: String, username: String) throws -> Credentials? {
|
||
|
|
||
|
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||
|
kSecAttrAccount as String: username,
|
||
|
kSecAttrServer as String: server,
|
||
|
kSecMatchLimit as String: kSecMatchLimitOne,
|
||
|
kSecReturnAttributes as String: true,
|
||
|
kSecReturnData as String: true]
|
||
|
|
||
|
var item: CFTypeRef?
|
||
|
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||
|
|
||
|
guard status != errSecItemNotFound else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
guard status == errSecSuccess else {
|
||
|
throw CredentialsError.unhandledError(status: status)
|
||
|
}
|
||
|
|
||
|
guard let existingItem = item as? [String : Any],
|
||
|
let passwordData = existingItem[kSecValueData as String] as? Data,
|
||
|
let password = String(data: passwordData, encoding: String.Encoding.utf8) else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return Credentials.readerAPIAuthLogin(username: username, apiKey: password)
|
||
|
|
||
|
}
|
||
|
|
||
|
public static func removeReaderAPIAuthCredentials(server: String, username: String) throws {
|
||
|
|
||
|
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||
|
kSecAttrAccount as String: username,
|
||
|
kSecAttrServer as String: server,
|
||
|
kSecMatchLimit as String: kSecMatchLimitOne,
|
||
|
kSecReturnAttributes as String: true,
|
||
|
kSecReturnData as String: true]
|
||
|
|
||
|
let status = SecItemDelete(query as CFDictionary)
|
||
|
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||
|
throw CredentialsError.unhandledError(status: status)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MARK: Private
|
||
|
|
||
|
extension CredentialsManager {
|
||
|
|
||
|
static func storeBasicCredentials(server: String, username: String, password: String) throws {
|
||
|
|
||
|
let passwordData = password.data(using: String.Encoding.utf8)!
|
||
|
|
||
|
let updateQuery: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||
|
kSecAttrAccount as String: username,
|
||
|
kSecAttrServer as String: server]
|
||
|
let attributes: [String: Any] = [kSecValueData as String: passwordData]
|
||
|
let status = SecItemUpdate(updateQuery as CFDictionary, attributes as CFDictionary)
|
||
|
|
||
|
switch status {
|
||
|
case errSecSuccess:
|
||
|
return
|
||
|
case errSecItemNotFound:
|
||
|
break
|
||
|
default:
|
||
|
throw CredentialsError.unhandledError(status: status)
|
||
|
}
|
||
|
|
||
|
guard status == errSecItemNotFound else {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let addQuery: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||
|
kSecAttrAccount as String: username,
|
||
|
kSecAttrServer as String: server,
|
||
|
kSecValueData as String: passwordData]
|
||
|
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
||
|
if addStatus != errSecSuccess {
|
||
|
throw CredentialsError.unhandledError(status: status)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|