Skip to content

Commit

Permalink
Merge pull request #108 from matthewpalmer/2.0.7
Browse files Browse the repository at this point in the history
2.0.7
  • Loading branch information
matthewpalmer committed Feb 14, 2016
2 parents c29ebcd + 2f54712 commit 80df919
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Locksmith.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Locksmith"
s.version = "2.0.2"
s.version = "2.0.7"
s.summary = "Locksmith is a powerful, protocol-oriented library for working with the keychain in Swift."
s.description = <<-DESC
Locksmith is a powerful, protocol-oriented library for working with the iOS, Mac OS X, watchOS, and tvOS keychain in Swift. It provides extensive support for a lot of different keychain requests, and extensively uses Swift-native concepts.
Expand Down
39 changes: 31 additions & 8 deletions Source/Locksmith.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,14 @@ public struct Locksmith {
}

public static func updateData(data: [String: AnyObject], forUserAccount userAccount: String, inService service: String = LocksmithDefaultService) throws {
// Delete and then re-save
do {
try Locksmith.deleteDataForUserAccount(userAccount, inService: service)
} catch {
// Deletion is likely to fail if the piece of data doesn't exist yet.
// This doesn't matter--we only tell the user about errors on the save request.
struct UpdateRequest: GenericPasswordSecureStorable, CreateableSecureStorable {
let service: String
let account: String
let data: [String: AnyObject]
}

return try Locksmith.saveData(data, forUserAccount: userAccount, inService: service)

let request = UpdateRequest(service: service, account: userAccount, data: data)
try request.updateInSecureStore()
}
}

Expand Down Expand Up @@ -424,6 +423,7 @@ public protocol CreateableSecureStorable: SecureStorable {
var data: [String: AnyObject] { get }
var performCreateRequestClosure: PerformRequestClosureType { get }
func createInSecureStore() throws
func updateInSecureStore() throws
}

// MARK: - ReadableSecureStorable
Expand Down Expand Up @@ -509,6 +509,23 @@ public protocol DeleteableSecureStorable: SecureStorable {

// MARK: - Default property dictionaries

extension CreateableSecureStorable {
func updateInSecureStore(query: [String: AnyObject]) throws {
var attributesToUpdate = query
attributesToUpdate[String(kSecClass)] = nil

let status = SecItemUpdate(query, attributesToUpdate)

if let error = LocksmithError(fromStatusCode: Int(status)) {
throw error
}

if status != errSecSuccess {
throw LocksmithError.Undefined
}
}
}

public extension CreateableSecureStorable where Self : GenericPasswordSecureStorable {
var asCreateableSecureStoragePropertyDictionary: [String: AnyObject] {
var old = genericPasswordBaseStoragePropertyDictionary
Expand All @@ -521,6 +538,9 @@ public extension CreateableSecureStorable where Self : GenericPasswordSecureStor
func createInSecureStore() throws {
try performSecureStorageAction(performCreateRequestClosure, secureStoragePropertyDictionary: asCreateableSecureStoragePropertyDictionary)
}
func updateInSecureStore() throws {
try self.updateInSecureStore(self.asCreateableSecureStoragePropertyDictionary)
}
}

public extension CreateableSecureStorable where Self : InternetPasswordSecureStorable {
Expand All @@ -543,6 +563,9 @@ public extension CreateableSecureStorable where Self : InternetPasswordSecureSto
func createInSecureStore() throws {
try performSecureStorageAction(performCreateRequestClosure, secureStoragePropertyDictionary: asCreateableSecureStoragePropertyDictionary)
}
func updateInSecureStore() throws {
try self.updateInSecureStore(self.asCreateableSecureStoragePropertyDictionary)
}
}

public extension DeleteableSecureStorable {
Expand Down
49 changes: 39 additions & 10 deletions Tests/LocksmithTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ class LocksmithTests: XCTestCase {
createGenericPasswordWithData(data)
}

func testUpdateForGenericPassword() {
let data = ["some": "data"]

struct CreateGenericPassword: CreateableSecureStorable, GenericPasswordSecureStorable, ReadableSecureStorable {
var data: [String: AnyObject]
let account: String
let service: String
}

var create = CreateGenericPassword(data: data, account: userAccount, service: service)
try! create.createInSecureStore() // make sure it doesn't throw
create.data = ["other": "data"]
try! create.updateInSecureStore()

let read = create.readFromSecureStore()!.data as! [String: String]
XCTAssertEqual(read, ["other": "data"])
}

func testLoadForGenericPassword() {
let data = ["one": "two"]
createGenericPasswordWithData(data)
Expand Down Expand Up @@ -239,7 +257,7 @@ class LocksmithTests: XCTestCase {
func testInternetPasswordMetaAttributesAreCreatedAndReturned() {
struct CreateInternetPassword: CreateableSecureStorable, InternetPasswordSecureStorable {
let account: String
let data: [String: AnyObject]
var data: [String: AnyObject]
let server: String
let port: Int
let internetProtocol: LocksmithInternetProtocol
Expand All @@ -265,20 +283,31 @@ class LocksmithTests: XCTestCase {
let authenticationType: LocksmithInternetAuthenticationType
}

let c = CreateInternetPassword(account: userAccount, data: initialData, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType, path: path, securityDomain: securityDomain)
var c = CreateInternetPassword(account: userAccount, data: initialData, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType, path: path, securityDomain: securityDomain)
try! c.createInSecureStore()

func assertResultMetadataIsOk(result: InternetPasswordSecureStorableResultType?) {
XCTAssertEqual(result?.account, userAccount)
XCTAssertEqual(result?.server, server)
XCTAssertEqual(result?.port, port)
XCTAssertEqual(result?.internetProtocol, internetProtocol)
XCTAssertEqual(result?.authenticationType, authenticationType)
XCTAssertEqual(result?.securityDomain, securityDomain)
XCTAssertEqual(result?.path, path)
}

let r = ReadInternetPassword(account: userAccount, server: server, port: port, internetProtocol: internetProtocol, authenticationType: authenticationType)
let result = r.readFromSecureStore()

XCTAssertEqual(result?.account, userAccount)
XCTAssertEqual(result!.data as! [String: String], initialData)
XCTAssertEqual(result?.server, server)
XCTAssertEqual(result?.port, port)
XCTAssertEqual(result?.internetProtocol, internetProtocol)
XCTAssertEqual(result?.authenticationType, authenticationType)
XCTAssertEqual(result?.securityDomain, securityDomain)
XCTAssertEqual(result?.path, path)
assertResultMetadataIsOk(result)

// Assert that metadata is maintained after an update
c.data = ["other internet": "junk"]
try! c.updateInSecureStore()

let result2 = r.readFromSecureStore()
XCTAssertEqual(result2!.data as! [String: String], ["other internet": "junk"])
assertResultMetadataIsOk(result2)
}

func assertStringPairsMatchInDictionary(dictionary: NSDictionary, pairs: [(key: CFString, expectedOutput: String)]) {
Expand Down

0 comments on commit 80df919

Please sign in to comment.