浏览代码

Merge pull request #450 from kishikawakatsumi/yangyubo-master

Yangyubo master
Kishikawa Katsumi 5 年之前
父节点
当前提交
19db427806
共有 2 个文件被更改,包括 131 次插入12 次删除
  1. 11 12
      Lib/KeychainAccess/Keychain.swift
  2. 120 0
      Lib/KeychainAccessTests/KeychainAccessTests.swift

+ 11 - 12
Lib/KeychainAccess/Keychain.swift

@@ -433,15 +433,16 @@ public final class Keychain {
         self.init(options)
     }
 
-    public convenience init(server: String, protocolType: ProtocolType, authenticationType: AuthenticationType = .default) {
-        self.init(server: URL(string: server)!, protocolType: protocolType, authenticationType: authenticationType)
+    public convenience init(server: String, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) {
+        self.init(server: URL(string: server)!, protocolType: protocolType, accessGroup: accessGroup, authenticationType: authenticationType)
     }
 
-    public convenience init(server: URL, protocolType: ProtocolType, authenticationType: AuthenticationType = .default) {
+    public convenience init(server: URL, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) {
         var options = Options()
         options.itemClass = .internetPassword
         options.server = server
         options.protocolType = protocolType
+        options.accessGroup = accessGroup
         options.authenticationType = authenticationType
         self.init(options)
     }
@@ -1003,15 +1004,16 @@ public final class Keychain {
             var item = [String: Any]()
 
             item["class"] = itemClass.description
+            
+            if let accessGroup = attributes[AttributeAccessGroup] as? String {
+                item["accessGroup"] = accessGroup
+            }
 
             switch itemClass {
             case .genericPassword:
                 if let service = attributes[AttributeService] as? String {
                     item["service"] = service
                 }
-                if let accessGroup = attributes[AttributeAccessGroup] as? String {
-                    item["accessGroup"] = accessGroup
-                }
             case .internetPassword:
                 if let server = attributes[AttributeServer] as? String {
                     item["server"] = server
@@ -1202,16 +1204,13 @@ extension Options {
 
         query[Class] = itemClass.rawValue
         query[AttributeSynchronizable] = SynchronizableAny
+        if let accessGroup = self.accessGroup {
+            query[AttributeAccessGroup] = accessGroup
+        }
 
         switch itemClass {
         case .genericPassword:
             query[AttributeService] = service
-            // Access group is not supported on any simulators.
-            #if (!arch(i386) && !arch(x86_64)) || (!os(iOS) && !os(watchOS) && !os(tvOS))
-            if let accessGroup = self.accessGroup {
-                query[AttributeAccessGroup] = accessGroup
-            }
-            #endif
         case .internetPassword:
             query[AttributeServer] = server.host
             query[AttributePort] = server.port

+ 120 - 0
Lib/KeychainAccessTests/KeychainAccessTests.swift

@@ -214,6 +214,126 @@ class KeychainAccessTests: XCTestCase {
         }
     }
 
+    func testInternetPasswordWithAccessGroup1() {
+        do {
+            // Add Keychain items
+            // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the
+            // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both.
+            // https://developer.apple.com/documentation/security/ksecattraccessgroup
+            let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true)
+            let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true)
+
+            do { try keychain.set("kishikawa_katsumi", key: "username") } catch {}
+            do { try keychain.set("password_1234", key: "password") } catch {}
+            do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {}
+            do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {}
+
+            XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi")
+            XCTAssertEqual(try! keychain.get("password"), "password_1234")
+
+            XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group")
+            XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group")
+        }
+
+        do {
+            // Update Keychain items
+            let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true)
+            let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true)
+
+            do { try keychain.set("katsumi_kishikawa", key: "username") } catch {}
+            do { try keychain.set("1234_password", key: "password") } catch {}
+            do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {}
+            do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {}
+
+            XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa")
+            XCTAssertEqual(try! keychain.get("password"), "1234_password")
+
+            XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group")
+            XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group")
+        }
+
+        do {
+            // Remove Keychain items
+            let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true)
+            let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true)
+
+            XCTAssertNotNil(try! keychainWithAccessGroup.get("username"))
+            XCTAssertNotNil(try! keychainWithAccessGroup.get("password"))
+
+            do { try keychainWithAccessGroup.remove("username") } catch {}
+            do { try keychainWithAccessGroup.remove("password") } catch {}
+
+
+            XCTAssertNil(try! keychainWithAccessGroup.get("username"))
+            XCTAssertNil(try! keychainWithAccessGroup.get("password"))
+
+            XCTAssertNotNil(try! keychain.get("username"))
+            XCTAssertNotNil(try! keychain.get("password"))
+
+            do { try keychain.remove("username") } catch {}
+            do { try keychain.remove("password") } catch {}
+
+            XCTAssertNil(try! keychain.get("username"))
+            XCTAssertNil(try! keychain.get("password"))
+        }
+    }
+
+    func testInternetPasswordWithAccessGroup2() {
+        do {
+            // Add Keychain items
+            let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true)
+            let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true)
+
+            do { try keychain.set("kishikawa_katsumi", key: "username") } catch {}
+            do { try keychain.set("password_1234", key: "password") } catch {}
+            do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {}
+            do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {}
+
+            XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi")
+            XCTAssertEqual(try! keychain.get("password"), "password_1234")
+
+            XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group")
+            XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group")
+        }
+
+        do {
+            // Update Keychain items
+            let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true)
+            let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true)
+
+            do { try keychain.set("katsumi_kishikawa", key: "username") } catch {}
+            do { try keychain.set("1234_password", key: "password") } catch {}
+            do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {}
+            do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {}
+
+            XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa")
+            XCTAssertEqual(try! keychain.get("password"), "1234_password")
+
+            XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group")
+            XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group")
+        }
+
+        do {
+            // Remove Keychain items
+            let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true)
+            let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true)
+
+            XCTAssertNotNil(try! keychainWithAccessGroup.get("username"))
+            XCTAssertNotNil(try! keychainWithAccessGroup.get("password"))
+
+            do { try keychain.remove("username") } catch {}
+            do { try keychain.remove("password") } catch {}
+
+            // If the access group is empty, the query will match all access group. So delete all values in other access groups.
+            XCTAssertNil(try! keychain.get("username"))
+            XCTAssertNil(try! keychain.get("password"))
+
+            XCTAssertNil(try! keychainWithAccessGroup.get("username"))
+            XCTAssertNil(try! keychainWithAccessGroup.get("password"))
+        }
+    }
+
+
     // MARK:
 
     func testDefaultInitializer() {