Skip to content

Commit

Permalink
Merge pull request #34 from cladvd/feature/set-keychain-accessibility
Browse files Browse the repository at this point in the history
[set-keychain-accessibility] - accessibility;
  • Loading branch information
Jan authored Apr 13, 2022
2 parents 53b0321 + c39e749 commit ce71337
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 33 deletions.
60 changes: 39 additions & 21 deletions iostests/Sources/DebugViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,62 @@ import UIKit
import kvault

class DebugViewController: UIViewController {

private let keyA = "A"
private let keyB = "B"
private let keyC = "C"
private let keyD = "D"
private let keyE = "E"
private let keyF = "F"

let button = UIButton()

override func viewDidLoad() {
super.viewDidLoad()

title = "test"
view.backgroundColor = .white
view.addSubview(button)

button.translatesAutoresizingMaskIntoConstraints = false
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.setTitle("DEBUG", for: .normal)
button.setTitleColor(.red, for: .normal)

button.addTarget(self, action: #selector(debug), for: .touchUpInside)
}

@objc func debug() {
let vault = KVault(serviceName: "DEBUG", accessGroup: nil)
let vault = KVault(serviceName: "DEBUG", accessGroup: nil, accessibility: KVault.Accessible.afterfirstunlock)
self.performOperations(on: vault)
}

private func performOperations(on vault: KVault) {
vault.set(key: "A", boolValue: true)
vault.set(key: "B", intValue: 1)
vault.set(key: "C", floatValue: 1)
vault.set(key: "D", doubleValue: 1)
vault.set(key: "E", longValue: 1)
vault.set(key: "F", stringValue: "T")
_ = vault.existsObject(forKey: "A")
_ = vault.bool(forKey: "A")
_ = vault.int(forKey: "B")
_ = vault.float(forKey: "C")
_ = vault.double(forKey: "D")
_ = vault.long(forKey: "E")
vault.deleteObject(forKey: "F")
vault.set(key: keyA, boolValue: true)
print("Value for \(keyA): \(vault.bool(forKey: keyA) ?? false)")

vault.set(key: keyB, intValue: 1)
print("Value for \(keyB): \(vault.int(forKey: keyB) ?? -1)")

vault.set(key: keyC, floatValue: 1)
print("Value for \(keyC): \(vault.float(forKey: keyC) ?? -1.0)")

vault.set(key: keyD, doubleValue: 1)
print("Value for \(keyD): \(vault.double(forKey: keyD) ?? -1.0)")

vault.set(key: keyE, longValue: 1)
print("Value for \(keyE): \(vault.long(forKey: keyE) ?? -1)")

vault.set(key: keyF, stringValue: "T")
print("Value for \(keyF): \(vault.string(forKey: keyF) ?? "NA")")

_ = vault.existsObject(forKey: keyA)
_ = vault.bool(forKey: keyA)
_ = vault.int(forKey: keyB)
_ = vault.float(forKey: keyC)
_ = vault.double(forKey: keyD)
_ = vault.long(forKey: keyE)
vault.deleteObject(forKey: keyF)
vault.clear()
}
}
6 changes: 3 additions & 3 deletions iostests/Sources/Keychain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ public struct Keychain {
// MARK: - Keychain

public static var `default`: KVault {
KVault(serviceName: "com.liftric.default", accessGroup: nil)
KVault(serviceName: "com.liftric.default", accessGroup: nil, accessibility: KVault.Accessible.afterfirstunlock)
}

public static var scoped: KVault {
KVault(serviceName: "com.liftric.test", accessGroup: nil)
KVault(serviceName: "com.liftric.test", accessGroup: nil, accessibility: KVault.Accessible.afterfirstunlock)
}

public static var global: KVault {
KVault(serviceName: nil, accessGroup: nil)
KVault(serviceName: nil, accessGroup: nil, accessibility: KVault.Accessible.afterfirstunlock)
}
}
38 changes: 29 additions & 9 deletions src/iosMain/kotlin/com/liftric/kvault/KVault.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,29 @@ import platform.darwin.noErr
*
* @param serviceName Name of the service. Used to categories entries.
* @param accessGroup Name of the access group. Used to share entries between apps.
* @param accessibility Level of the accessibility for the Keychain instance.
* @constructor Initiates a Keychain with the given parameters.
*/
actual open class KVault(
val serviceName: String? = null,
val accessGroup: String? = null
val accessGroup: String? = null,
val accessibility: Accessible = Accessible.WhenUnlocked
) {
/**
* kSecAttrAccessible attributes wrapper.
* attribute enables you to control item availability relative to the lock state of the device.
* It also lets you specify eligibility for restoration to a new device.
* If the attribute ends with the string ThisDeviceOnly, the item can be restored to the same device
* that created a backup, but it isn’t migrated when restoring another device’s backup data.
*/
enum class Accessible(val value: CFStringRef?) {
WhenPasscodeSetThisDeviceOnly(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly),
WhenUnlockedThisDeviceOnly(kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
WhenUnlocked(kSecAttrAccessibleWhenUnlocked),
AfterFirstUnlock(kSecAttrAccessibleAfterFirstUnlock),
AfterFirstUnlockThisDeviceOnly(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
}

/**
* Saves a string value in the Keychain.
* @param key The key to store
Expand Down Expand Up @@ -166,8 +183,8 @@ actual open class KVault(
val query = query(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to account,
kSecReturnData to kCFBooleanFalse
)
kSecReturnData to kCFBooleanFalse,
)

SecItemCopyMatching(query, null)
.validate()
Expand Down Expand Up @@ -208,7 +225,7 @@ actual open class KVault(
// ===============

private fun addOrUpdate(key: String, value: NSData?): Boolean {
return if(existsObject(key)) {
return if (existsObject(key)) {
update(key, value)
} else {
add(key, value)
Expand All @@ -219,18 +236,19 @@ actual open class KVault(
val query = query(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to account,
kSecValueData to data
kSecValueData to data,
kSecAttrAccessible to accessibility.value
)

SecItemAdd(query, null)
.validate()

}

private fun update(key: String, value: Any?): Boolean = context(key, value) { (account, data) ->
val query = query(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to account,
kSecReturnData to kCFBooleanFalse
kSecReturnData to kCFBooleanFalse,
)

val updateQuery = query(
Expand All @@ -246,7 +264,7 @@ actual open class KVault(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to account,
kSecReturnData to kCFBooleanTrue,
kSecMatchLimit to kSecMatchLimitOne
kSecMatchLimit to kSecMatchLimitOne,
)

memScoped {
Expand Down Expand Up @@ -284,7 +302,9 @@ actual open class KVault(
}
}

private fun String.toNSData(): NSData? = NSString.create(string = this).dataUsingEncoding(NSUTF8StringEncoding)
private fun String.toNSData(): NSData? =
NSString.create(string = this).dataUsingEncoding(NSUTF8StringEncoding)

private fun NSNumber.toNSData() = NSKeyedArchiver.archivedDataWithRootObject(this)
private fun NSData.toNSNumber() = NSKeyedUnarchiver.unarchiveObjectWithData(this) as? NSNumber

Expand Down

0 comments on commit ce71337

Please sign in to comment.