Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[set-keychain-accessibility] - accessibility; #34

Merged
merged 6 commits into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}
39 changes: 30 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 @@ -142,8 +159,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 @@ -184,7 +201,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 @@ -195,18 +212,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 @@ -222,7 +240,8 @@ actual open class KVault(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to account,
kSecReturnData to kCFBooleanTrue,
kSecMatchLimit to kSecMatchLimitOne
kSecMatchLimit to kSecMatchLimitOne,
kSecAttrAccessible to accessibility.value
benjohnde marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

@gaebel gaebel Apr 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the accessibility to the value (read) query will only narrow the results down, you can remove it.

)

memScoped {
Expand Down Expand Up @@ -260,7 +279,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