blob: b363a7f517691b0e0080a1fc578a0836aefb1e6f [file] [log] [blame]
//
// UsergridKeychainHelpers.swift
// UsergridSDK
//
// Created by Robert Walsh on 12/21/15.
//
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*
*/
import Foundation
#if os(iOS) || os(tvOS) || os(watchOS)
import UIKit
#endif
private let USERGRID_KEYCHAIN_NAME = "Usergrid"
private let USERGRID_DEVICE_KEYCHAIN_SERVICE = "SharedDevice"
private let USERGRID_CURRENT_USER_KEYCHAIN_SERVICE = "CurrentUser"
private func usergridGenericKeychainItem() -> [String:Any] {
var keychainItem: [String:Any] = [:]
keychainItem[kSecClass as String] = kSecClassGenericPassword as String
keychainItem[kSecAttrAccessible as String] = kSecAttrAccessibleAlways as String
keychainItem[kSecAttrAccount as String] = USERGRID_KEYCHAIN_NAME
return keychainItem
}
internal extension UsergridDevice {
static func deviceKeychainItem() -> [String:Any] {
var keychainItem = usergridGenericKeychainItem()
keychainItem[kSecAttrService as String] = USERGRID_DEVICE_KEYCHAIN_SERVICE
return keychainItem
}
static func createNewDeviceKeychainUUID() -> String {
#if os(watchOS) || os(OSX)
let usergridUUID = NSUUID().uuidString
#elseif os(iOS) || os(tvOS)
let usergridUUID = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
#endif
return usergridUUID
}
private static func createNewSharedDevice() -> UsergridDevice {
var deviceEntityDict = UsergridDevice.commonDevicePropertyDict()
deviceEntityDict[UsergridEntityProperties.uuid.stringValue] = UsergridDevice.createNewDeviceKeychainUUID()
let sharedDevice = UsergridDevice(type: UsergridDevice.DEVICE_ENTITY_TYPE, name: nil, propertyDict: deviceEntityDict)
return sharedDevice
}
static func getOrCreateSharedDeviceFromKeychain() -> UsergridDevice {
var queryAttributes = UsergridDevice.deviceKeychainItem()
queryAttributes[kSecReturnData as String] = (kCFBooleanTrue != nil) as Bool
queryAttributes[kSecReturnAttributes as String] = (kCFBooleanTrue != nil) as Bool
var result: AnyObject?
let status = withUnsafeMutablePointer(to: &result) { SecItemCopyMatching(queryAttributes as CFDictionary, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let resultDictionary = result as? NSDictionary {
if let resultData = resultDictionary[kSecValueData as String] as? Data {
if let sharedDevice = NSKeyedUnarchiver.unarchiveObject(with: resultData) as? UsergridDevice {
return sharedDevice
} else {
UsergridDevice.deleteSharedDeviceKeychainItem()
}
}
}
}
let sharedDevice = UsergridDevice.createNewSharedDevice()
UsergridDevice.saveSharedDeviceKeychainItem(sharedDevice)
return sharedDevice
}
static func saveSharedDeviceKeychainItem(_ device:UsergridDevice) {
var queryAttributes = UsergridDevice.deviceKeychainItem()
queryAttributes[kSecReturnData as String] = (kCFBooleanTrue != nil) as Bool
queryAttributes[kSecReturnAttributes as String] = (kCFBooleanTrue != nil) as Bool
let sharedDeviceData = NSKeyedArchiver.archivedData(withRootObject: device);
if SecItemCopyMatching(queryAttributes as CFDictionary,nil) == errSecSuccess // Do we need to update keychain item or add a new one.
{
let attributesToUpdate = [kSecValueData as String:sharedDeviceData]
let updateStatus = SecItemUpdate(UsergridDevice.deviceKeychainItem() as CFDictionary, attributesToUpdate as CFDictionary)
if updateStatus != errSecSuccess {
print("Error updating shared device data to keychain!")
}
}
else
{
var keychainItem = UsergridDevice.deviceKeychainItem()
keychainItem[kSecValueData as String] = sharedDeviceData
let status = SecItemAdd(keychainItem as CFDictionary, nil)
if status != errSecSuccess {
print("Error adding shared device data to keychain!")
}
}
}
static func deleteSharedDeviceKeychainItem() {
var queryAttributes = UsergridDevice.deviceKeychainItem()
queryAttributes[kSecReturnData as String] = (kCFBooleanFalse != nil) as Bool
queryAttributes[kSecReturnAttributes as String] = (kCFBooleanFalse != nil) as Bool
if SecItemCopyMatching(queryAttributes as CFDictionary,nil) == errSecSuccess {
let deleteStatus = SecItemDelete(queryAttributes as CFDictionary)
if deleteStatus != errSecSuccess {
print("Error deleting shared device data to keychain!")
}
}
}
}
internal extension UsergridUser {
static func userKeychainItem(_ client:UsergridClient) -> [String:Any] {
var keychainItem = usergridGenericKeychainItem()
keychainItem[kSecAttrService as String] = USERGRID_CURRENT_USER_KEYCHAIN_SERVICE + "." + client.appId + "." + client.orgId
return keychainItem
}
static func getCurrentUserFromKeychain(_ client:UsergridClient) -> UsergridUser? {
var queryAttributes = UsergridUser.userKeychainItem(client)
queryAttributes[kSecReturnData as String] = (kCFBooleanTrue != nil) as Bool
queryAttributes[kSecReturnAttributes as String] = (kCFBooleanTrue != nil) as Bool
var result: AnyObject?
let status = withUnsafeMutablePointer(to: &result) { SecItemCopyMatching(queryAttributes as CFDictionary, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let resultDictionary = result as? NSDictionary {
if let resultData = resultDictionary[kSecValueData as String] as? Data {
if let currentUser = NSKeyedUnarchiver.unarchiveObject(with: resultData) as? UsergridUser {
return currentUser
}
}
}
}
return nil
}
static func saveCurrentUserKeychainItem(_ client:UsergridClient, currentUser:UsergridUser) {
var queryAttributes = UsergridUser.userKeychainItem(client)
queryAttributes[kSecReturnData as String] = (kCFBooleanTrue != nil) as Bool
queryAttributes[kSecReturnAttributes as String] = (kCFBooleanTrue != nil) as Bool
if SecItemCopyMatching(queryAttributes as CFDictionary,nil) == errSecSuccess // Do we need to update keychain item or add a new one.
{
let attributesToUpdate = [kSecValueData as String:NSKeyedArchiver.archivedData(withRootObject: currentUser)]
let updateStatus = SecItemUpdate(UsergridUser.userKeychainItem(client) as CFDictionary, attributesToUpdate as CFDictionary)
if updateStatus != errSecSuccess {
print("Error updating current user data to keychain!")
}
}
else
{
var keychainItem = UsergridUser.userKeychainItem(client)
keychainItem[kSecValueData as String] = NSKeyedArchiver.archivedData(withRootObject: currentUser)
let status = SecItemAdd(keychainItem as CFDictionary, nil)
if status != errSecSuccess {
print("Error adding current user data to keychain!")
}
}
}
static func deleteCurrentUserKeychainItem(_ client:UsergridClient) {
var queryAttributes = UsergridUser.userKeychainItem(client)
queryAttributes[kSecReturnData as String] = (kCFBooleanFalse != nil) as Bool
queryAttributes[kSecReturnAttributes as String] = (kCFBooleanFalse != nil) as Bool
if SecItemCopyMatching(queryAttributes as CFDictionary,nil) == errSecSuccess {
let deleteStatus = SecItemDelete(queryAttributes as CFDictionary)
if deleteStatus != errSecSuccess {
print("Error deleting current user data to keychain!")
}
}
}
}