Skip to content

Commit 5e3b0b5

Browse files
committed
Return value after storing SecDataConvertible
1 parent 1c58405 commit 5e3b0b5

File tree

3 files changed

+91
-33
lines changed

3 files changed

+91
-33
lines changed

Sources/SwiftSecurity/Keychain/Keychain.swift

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@ extension Keychain: SecDataStore {
184184
public func store<T: SecDataConvertible>(_ data: T, query: SecItemQuery<GenericPassword>, accessPolicy: AccessPolicy = .default) throws {
185185
try store(.data(data.rawRepresentation), query: query, accessPolicy: accessPolicy)
186186
}
187+
188+
public func store<T: SecDataConvertible>(
189+
_ data: T,
190+
returning returnType: SecReturnType,
191+
query: SecItemQuery<GenericPassword>,
192+
accessPolicy: AccessPolicy = .default
193+
) throws -> SecValue<GenericPassword>? {
194+
try store(.data(data.rawRepresentation), returning: returnType, query: query, accessPolicy: accessPolicy)
195+
}
187196

188197
public func retrieve<T: SecDataConvertible>(_ query: SecItemQuery<GenericPassword>, authenticationContext: LAContext? = nil) throws -> T? {
189198
if let value = try retrieve(.data, query: query, authenticationContext: authenticationContext), case let .data(data) = value {
@@ -334,12 +343,31 @@ extension Keychain: SecIdentityStore {
334343
// MARK: - Private
335344

336345
private extension Keychain {
337-
func store<SecItem>(_ value: SecValue<SecItem>, query: SecItemQuery<SecItem>, accessPolicy: AccessPolicy) throws {
346+
@discardableResult
347+
func store<SecItem>(
348+
_ value: SecValue<SecItem>,
349+
returning returnType: SecReturnType = [],
350+
query: SecItemQuery<SecItem>,
351+
accessPolicy: AccessPolicy = .default
352+
) throws -> SecValue<SecItem>? {
338353
var query = query
339354
query[.accessGroup] = accessGroup.rawValue
340355
query[.accessControl] = try accessPolicy.accessControl
341356
query[.accessible] = accessPolicy.accessibility
342357

358+
if returnType.contains(.data) {
359+
query[kSecReturnData as String] = true
360+
}
361+
if returnType.contains(.info) {
362+
query[kSecReturnAttributes as String] = true
363+
}
364+
if returnType.contains(.reference) {
365+
query[kSecReturnRef as String] = true
366+
}
367+
if returnType.contains(.persistentReference) {
368+
query[kSecReturnPersistentRef as String] = true
369+
}
370+
343371
switch value {
344372
case .data(let data):
345373
query[kSecValueData as String] = data
@@ -349,9 +377,35 @@ private extension Keychain {
349377
throw SwiftSecurityError.invalidParameter
350378
}
351379

352-
switch SecItemAdd(query.rawValue as CFDictionary, nil) {
380+
var result: AnyObject?
381+
switch SecItemAdd(query.rawValue as CFDictionary, &result) {
353382
case errSecSuccess:
354-
return
383+
switch returnType {
384+
case .data:
385+
if let data = result as? Data {
386+
return .data(data)
387+
} else {
388+
return nil
389+
}
390+
case .reference:
391+
if let result {
392+
return .reference(result)
393+
} else {
394+
return nil
395+
}
396+
case .persistentReference:
397+
if let data = result as? Data {
398+
return .persistentReference(data)
399+
} else {
400+
return nil
401+
}
402+
default:
403+
if let attributes = result as? [String: Any] {
404+
return .dictionary(SecItemInfo<SecItem>(rawValue: attributes))
405+
} else {
406+
return nil
407+
}
408+
}
355409
case let status:
356410
throw SwiftSecurityError(rawValue: status)
357411
}

Sources/SwiftSecurity/Keychain/SecItemStore/SecItemStore.swift

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,46 @@ public protocol SecItemStore {
1616
func removeAll() throws
1717
}
1818

19-
// MARK: - SecData
19+
public extension SecItemStore {
20+
func info<SecItem>(for query: SecItemQuery<SecItem>, authenticationContext: LAContext? = nil) throws -> SecItemInfo<SecItem>? {
21+
if let value = try retrieve(.info, query: query, authenticationContext: authenticationContext), case let .dictionary(info) = value {
22+
return info
23+
} else {
24+
return nil
25+
}
26+
}
27+
}
28+
29+
// MARK: - Data
2030

2131
public protocol SecDataStore: SecItemStore {
22-
// MARK: - Generic
32+
// MARK: - Generic Password
2333

24-
func store<T: SecDataConvertible>(_ data: T, query: SecItemQuery<GenericPassword>, accessPolicy: AccessPolicy) throws
34+
func store<T: SecDataConvertible>(_ data: T, returning returnType: SecReturnType, query: SecItemQuery<GenericPassword>, accessPolicy: AccessPolicy) throws -> SecValue<GenericPassword>?
2535
func retrieve<T: SecDataConvertible>(_ query: SecItemQuery<GenericPassword>, authenticationContext: LAContext?) throws -> T?
2636
func remove(_ query: SecItemQuery<GenericPassword>) throws -> Bool
2737

28-
// MARK: - Internet
38+
// MARK: - Internet Password
2939

3040
func store<T: SecDataConvertible>(_ data: T, query: SecItemQuery<InternetPassword>, accessPolicy: AccessPolicy) throws
3141
func retrieve<T: SecDataConvertible>(_ query: SecItemQuery<InternetPassword>, authenticationContext: LAContext?) throws -> T?
3242
func remove(_ query: SecItemQuery<InternetPassword>) throws -> Bool
3343
}
3444

45+
public extension SecDataStore {
46+
func store<T: SecDataConvertible>(_ data: T, query: SecItemQuery<GenericPassword>, accessPolicy: AccessPolicy) throws {
47+
try self.store(data, returning: [], query: query, accessPolicy: accessPolicy)
48+
}
49+
50+
func retrieve(_ query: SecItemQuery<GenericPassword>) throws -> Data? {
51+
try self.retrieve<Data>(query, authenticationContext: nil)
52+
}
53+
54+
func retrieve(_ query: SecItemQuery<InternetPassword>) throws -> Data? {
55+
try self.retrieve<Data>(query, authenticationContext: nil)
56+
}
57+
}
58+
3559
// MARK: - SecKey
3660

3761
public protocol SecKeyStore: SecItemStore {
@@ -56,23 +80,3 @@ public protocol SecIdentityStore: SecItemStore {
5680
func retrieve(_ query: SecItemQuery<SecIdentity>, authenticationContext: LAContext?) throws -> SecIdentity?
5781
func remove(_ query: SecItemQuery<SecIdentity>) throws -> Bool
5882
}
59-
60-
// MARK: - Convenient
61-
62-
public extension SecDataStore {
63-
func info<SecItem>(for query: SecItemQuery<SecItem>, authenticationContext: LAContext? = nil) throws -> SecItemInfo<SecItem>? {
64-
if let value = try retrieve(.info, query: query, authenticationContext: authenticationContext), case let .dictionary(info) = value {
65-
return info
66-
} else {
67-
return nil
68-
}
69-
}
70-
71-
func retrieve(_ query: SecItemQuery<GenericPassword>) throws -> Data? {
72-
try self.retrieve<Data>(query, authenticationContext: nil)
73-
}
74-
75-
func retrieve(_ query: SecItemQuery<InternetPassword>) throws -> Data? {
76-
try self.retrieve<Data>(query, authenticationContext: nil)
77-
}
78-
}

Tests/SwiftSecurityTests/AccessPolicyTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,35 @@ import Security
1313
final class AccessPolicyTests: XCTestCase {
1414
func testAccessibility() throws {
1515
do {
16-
let accessPolicy = SecAccessPolicy(.whenPasscodeSetThisDeviceOnly)
16+
let accessPolicy = AccessPolicy(.whenPasscodeSetThisDeviceOnly)
1717
XCTAssertEqual(accessPolicy.accessibility, String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly))
1818
XCTAssertNil(try accessPolicy.accessControl)
1919
}
2020
do {
21-
let accessPolicy = SecAccessPolicy(.whenUnlocked)
21+
let accessPolicy = AccessPolicy(.whenUnlocked)
2222
XCTAssertEqual(accessPolicy.accessibility, String(kSecAttrAccessibleWhenUnlocked))
2323
XCTAssertNil(try accessPolicy.accessControl)
2424
}
2525
do {
26-
let accessPolicy = SecAccessPolicy(.whenUnlockedThisDeviceOnly)
26+
let accessPolicy = AccessPolicy(.whenUnlockedThisDeviceOnly)
2727
XCTAssertEqual(accessPolicy.accessibility, String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly))
2828
XCTAssertNil(try accessPolicy.accessControl)
2929
}
3030
do {
31-
let accessPolicy = SecAccessPolicy(.afterFirstUnlock)
31+
let accessPolicy = AccessPolicy(.afterFirstUnlock)
3232
XCTAssertEqual(accessPolicy.accessibility, String(kSecAttrAccessibleAfterFirstUnlock))
3333
XCTAssertNil(try accessPolicy.accessControl)
3434
}
3535
do {
36-
let accessPolicy = SecAccessPolicy(.afterFirstUnlockThisDeviceOnly)
36+
let accessPolicy = AccessPolicy(.afterFirstUnlockThisDeviceOnly)
3737
XCTAssertEqual(accessPolicy.accessibility, String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly))
3838
XCTAssertNil(try accessPolicy.accessControl)
3939
}
4040
}
4141

4242
func testAccessControl() {
4343
do {
44-
let accessPolicy = SecAccessPolicy(.afterFirstUnlock, options: .biometryAny)
44+
let accessPolicy = AccessPolicy(.afterFirstUnlock, options: .biometryAny)
4545
XCTAssertNotNil(try accessPolicy.accessControl)
4646
}
4747
}

0 commit comments

Comments
 (0)