Refactoring

This commit is contained in:
Justin Mazzocchi 2020-08-13 18:59:17 -07:00
parent 43ccc12468
commit 4bcb862065
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
9 changed files with 53 additions and 27 deletions

View file

@ -30,6 +30,10 @@ extension MockKeychainService: KeychainService {
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
fatalError("not implemented")
}
static func deleteKey(applicationTag: String) throws {
fatalError("not implemented")
}
}
private extension MockKeychainService {

View file

@ -89,7 +89,7 @@ private extension NotificationService {
let serverPublicKeyData = Data(base64Encoded: serverPublicKeyBase64)
else { throw NotificationServiceError.userInfoDataAbsent }
let secretsService = SecretsService(identityID: identityID, keychainServiceType: LiveKeychainService.self)
let secretsService = SecretsService(identityID: identityID, keychainService: LiveKeychainService.self)
guard
let auth = try secretsService.getPushAuth(),

View file

@ -31,7 +31,7 @@ extension IdentitiesService {
}
func authorizeIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Void, Error> {
let secretsService = SecretsService(identityID: id, keychainServiceType: environment.keychainServiceType)
let secretsService = SecretsService(identityID: id, keychainService: environment.keychainServiceType)
let authenticationService = AuthenticationService(environment: environment)
return authenticationService.authorizeApp(instanceURL: instanceURL)
@ -57,7 +57,7 @@ extension IdentitiesService {
.tryMap { _ -> Void in
try SecretsService(
identityID: id,
keychainServiceType: environment.keychainServiceType)
keychainService: environment.keychainServiceType)
.deleteAllItems()
return ()

View file

@ -32,7 +32,7 @@ class IdentityService {
self.identity = identity
secretsService = SecretsService(
identityID: identityID,
keychainServiceType: environment.keychainServiceType)
keychainService: environment.keychainServiceType)
networkClient = MastodonClient(session: environment.session)
networkClient.instanceURL = identity.url
networkClient.accessToken = try secretsService.item(.accessToken)

View file

@ -8,6 +8,7 @@ protocol KeychainService {
static func getGenericPassword(account: String, service: String) throws -> Data?
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data?
static func deleteKey(applicationTag: String) throws
}
struct LiveKeychainService {}
@ -104,6 +105,14 @@ extension LiveKeychainService: KeychainService {
throw NSError(status: status)
}
}
static func deleteKey(applicationTag: String) throws {
let status = SecItemDelete(keyQueryDictionary(applicationTag: applicationTag) as CFDictionary)
if status != errSecSuccess {
throw NSError(status: status)
}
}
}
private extension LiveKeychainService {

View file

@ -13,11 +13,11 @@ enum SecretsStorableError: Error {
struct SecretsService {
let identityID: UUID
private let keychainServiceType: KeychainService.Type
private let keychainService: KeychainService.Type
init(identityID: UUID, keychainServiceType: KeychainService.Type) {
init(identityID: UUID, keychainService: KeychainService.Type) {
self.identityID = identityID
self.keychainServiceType = keychainServiceType
self.keychainService = keychainService
}
}
@ -31,16 +31,30 @@ extension SecretsService {
}
}
extension SecretsService.Item {
enum Kind {
case genericPassword
case key
}
var kind: Kind {
switch self {
case .pushKey: return .key
default: return .genericPassword
}
}
}
extension SecretsService {
func set(_ data: SecretsStorable, forItem item: Item) throws {
try keychainServiceType.setGenericPassword(
try keychainService.setGenericPassword(
data: data.dataStoredInSecrets,
forAccount: key(item: item),
service: Self.keychainServiceName)
}
func item<T: SecretsStorable>(_ item: Item) throws -> T? {
guard let data = try keychainServiceType.getGenericPassword(
guard let data = try keychainService.getGenericPassword(
account: key(item: item),
service: Self.keychainServiceName) else {
return nil
@ -51,20 +65,25 @@ extension SecretsService {
func deleteAllItems() throws {
for item in SecretsService.Item.allCases {
try keychainServiceType.deleteGenericPassword(
switch item.kind {
case .genericPassword:
try keychainService.deleteGenericPassword(
account: key(item: item),
service: Self.keychainServiceName)
case .key:
try keychainService.deleteKey(applicationTag: key(item: item))
}
}
}
func generatePushKeyAndReturnPublicKey() throws -> Data {
try keychainServiceType.generateKeyAndReturnPublicKey(
try keychainService.generateKeyAndReturnPublicKey(
applicationTag: key(item: .pushKey),
attributes: PushKey.attributes)
}
func getPushKey() throws -> Data? {
try keychainServiceType.getPrivateKey(
try keychainService.getPrivateKey(
applicationTag: key(item: .pushKey),
attributes: PushKey.attributes)
}

View file

@ -72,7 +72,7 @@ extension RootViewModel {
func deleteIdentity(id: UUID) {
identitiesService.deleteIdentity(id: id)
.sink(receiveCompletion: { _ in }, receiveValue: {})
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}

View file

@ -9,29 +9,23 @@ class AddIdentityViewModelTests: XCTestCase {
func testAddIdentity() throws {
let identityDatabase = IdentityDatabase.fresh()
let sut = AddIdentityViewModel(identitiesService: .fresh(identityDatabase: identityDatabase))
let addedIDAndURLRecorder = sut.addedIdentityIDAndURL.record()
let addedIDRecorder = sut.addedIdentityID.record()
sut.urlFieldText = "https://mastodon.social"
sut.logInTapped()
let addedIdentityIDAndURL = try wait(for: addedIDAndURLRecorder.next(), timeout: 1)
// XCTAssertEqual(addedIdentityIDAndURL.0, addedIdentityID)
XCTAssertEqual(addedIdentityIDAndURL.1, URL(string: "https://mastodon.social")!)
_ = try wait(for: addedIDRecorder.next(), timeout: 1)
}
func testAddIdentityWithoutScheme() throws {
let identityDatabase = IdentityDatabase.fresh()
let sut = AddIdentityViewModel(identitiesService: .fresh(identityDatabase: identityDatabase))
let addedIDAndURLRecorder = sut.addedIdentityIDAndURL.record()
let addedIDRecorder = sut.addedIdentityID.record()
sut.urlFieldText = "mastodon.social"
sut.logInTapped()
let addedIdentityIDAndURL = try wait(for: addedIDAndURLRecorder.next(), timeout: 1)
// XCTAssertEqual(addedIdentityIDAndURL.0, addedIdentityID)
XCTAssertEqual(addedIdentityIDAndURL.1, URL(string: "https://mastodon.social")!)
_ = try wait(for: addedIDRecorder.next(), timeout: 1)
}
func testInvalidURL() throws {

View file

@ -20,8 +20,8 @@ class RootViewModelTests: XCTestCase {
let addIdentityViewModel = sut.addIdentityViewModel()
addIdentityViewModel.addedIdentityIDAndURL
.sink(receiveValue: sut.newIdentityCreated(id:instanceURL:))
addIdentityViewModel.addedIdentityID
.sink(receiveValue: sut.newIdentitySelected(id:))
.store(in: &cancellables)
addIdentityViewModel.urlFieldText = "https://mastodon.social"