FakeAuthKeychainStorage.swift 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. @testable import FirebaseAuth
  15. import FirebaseCoreInternal
  16. import Foundation
  17. import XCTest
  18. /// The utility class to update the real keychain
  19. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  20. final class FakeAuthKeychainStorage: AuthKeychainStorage {
  21. // Fake Keychain. It's a dictionary, keyed by service name, for each key-value store dictionary
  22. private let fakeKeychain = UnfairLock<[String: [String: Any]]>([:])
  23. private let fakeLegacyKeychain = UnfairLock<[String: Any]>([:])
  24. func get(query: [String: Any], result: inout AnyObject?) -> OSStatus {
  25. if let service = queryService(query) {
  26. guard let value = fakeKeychain.value()[service]?[queryKey(query)] else {
  27. return errSecItemNotFound
  28. }
  29. let returnArrayofDictionary = [[kSecValueData as String: value]]
  30. result = returnArrayofDictionary as AnyObject
  31. return noErr
  32. } else {
  33. guard let value = fakeLegacyKeychain.value()[queryKey(query)] else {
  34. return errSecItemNotFound
  35. }
  36. let returnArrayofDictionary = [[kSecValueData as String: value]]
  37. result = returnArrayofDictionary as AnyObject
  38. return noErr
  39. }
  40. }
  41. func add(query: [String: Any]) -> OSStatus {
  42. if let service = queryService(query) {
  43. fakeKeychain.withLock { $0[service]?[queryKey(query)] = query[kSecValueData as String] }
  44. } else {
  45. fakeLegacyKeychain.withLock { $0[queryKey(query)] = query[kSecValueData as String] }
  46. }
  47. return noErr
  48. }
  49. func update(query: [String: Any], attributes: [String: Any]) -> OSStatus {
  50. return add(query: query)
  51. }
  52. @discardableResult func delete(query: [String: Any]) -> OSStatus {
  53. if let service = queryService(query) {
  54. fakeKeychain.withLock { $0[service]?[queryKey(query)] = nil }
  55. } else {
  56. fakeLegacyKeychain.withLock { $0[queryKey(query)] = nil }
  57. }
  58. return noErr
  59. }
  60. private func queryKey(_ query: [String: Any]) -> String {
  61. do {
  62. return try XCTUnwrap(query[kSecAttrAccount as String] as? String)
  63. } catch {
  64. XCTFail("\(error)")
  65. return ""
  66. }
  67. }
  68. private func queryService(_ query: [String: Any]) -> String? {
  69. guard let service = query[kSecAttrService as String] as? String else {
  70. return nil
  71. }
  72. fakeKeychain.withLock { fakeKeychain in
  73. if fakeKeychain[service] == nil {
  74. fakeKeychain[service] = [:]
  75. }
  76. }
  77. return service
  78. }
  79. }