Refactoring
This commit is contained in:
parent
43ccc12468
commit
4bcb862065
|
@ -30,6 +30,10 @@ extension MockKeychainService: KeychainService {
|
||||||
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
|
static func getPrivateKey(applicationTag: String, attributes: [String: Any]) throws -> Data? {
|
||||||
fatalError("not implemented")
|
fatalError("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func deleteKey(applicationTag: String) throws {
|
||||||
|
fatalError("not implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MockKeychainService {
|
private extension MockKeychainService {
|
||||||
|
|
|
@ -89,7 +89,7 @@ private extension NotificationService {
|
||||||
let serverPublicKeyData = Data(base64Encoded: serverPublicKeyBase64)
|
let serverPublicKeyData = Data(base64Encoded: serverPublicKeyBase64)
|
||||||
else { throw NotificationServiceError.userInfoDataAbsent }
|
else { throw NotificationServiceError.userInfoDataAbsent }
|
||||||
|
|
||||||
let secretsService = SecretsService(identityID: identityID, keychainServiceType: LiveKeychainService.self)
|
let secretsService = SecretsService(identityID: identityID, keychainService: LiveKeychainService.self)
|
||||||
|
|
||||||
guard
|
guard
|
||||||
let auth = try secretsService.getPushAuth(),
|
let auth = try secretsService.getPushAuth(),
|
||||||
|
|
|
@ -31,7 +31,7 @@ extension IdentitiesService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func authorizeIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Void, Error> {
|
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)
|
let authenticationService = AuthenticationService(environment: environment)
|
||||||
|
|
||||||
return authenticationService.authorizeApp(instanceURL: instanceURL)
|
return authenticationService.authorizeApp(instanceURL: instanceURL)
|
||||||
|
@ -57,7 +57,7 @@ extension IdentitiesService {
|
||||||
.tryMap { _ -> Void in
|
.tryMap { _ -> Void in
|
||||||
try SecretsService(
|
try SecretsService(
|
||||||
identityID: id,
|
identityID: id,
|
||||||
keychainServiceType: environment.keychainServiceType)
|
keychainService: environment.keychainServiceType)
|
||||||
.deleteAllItems()
|
.deleteAllItems()
|
||||||
|
|
||||||
return ()
|
return ()
|
||||||
|
|
|
@ -32,7 +32,7 @@ class IdentityService {
|
||||||
self.identity = identity
|
self.identity = identity
|
||||||
secretsService = SecretsService(
|
secretsService = SecretsService(
|
||||||
identityID: identityID,
|
identityID: identityID,
|
||||||
keychainServiceType: environment.keychainServiceType)
|
keychainService: environment.keychainServiceType)
|
||||||
networkClient = MastodonClient(session: environment.session)
|
networkClient = MastodonClient(session: environment.session)
|
||||||
networkClient.instanceURL = identity.url
|
networkClient.instanceURL = identity.url
|
||||||
networkClient.accessToken = try secretsService.item(.accessToken)
|
networkClient.accessToken = try secretsService.item(.accessToken)
|
||||||
|
|
|
@ -8,6 +8,7 @@ protocol KeychainService {
|
||||||
static func getGenericPassword(account: String, service: String) throws -> Data?
|
static func getGenericPassword(account: String, service: String) throws -> Data?
|
||||||
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data
|
static func generateKeyAndReturnPublicKey(applicationTag: String, attributes: [String: Any]) throws -> Data
|
||||||
static func getPrivateKey(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 {}
|
struct LiveKeychainService {}
|
||||||
|
@ -104,6 +105,14 @@ extension LiveKeychainService: KeychainService {
|
||||||
throw NSError(status: status)
|
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 {
|
private extension LiveKeychainService {
|
||||||
|
|
|
@ -13,11 +13,11 @@ enum SecretsStorableError: Error {
|
||||||
|
|
||||||
struct SecretsService {
|
struct SecretsService {
|
||||||
let identityID: UUID
|
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.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 {
|
extension SecretsService {
|
||||||
func set(_ data: SecretsStorable, forItem item: Item) throws {
|
func set(_ data: SecretsStorable, forItem item: Item) throws {
|
||||||
try keychainServiceType.setGenericPassword(
|
try keychainService.setGenericPassword(
|
||||||
data: data.dataStoredInSecrets,
|
data: data.dataStoredInSecrets,
|
||||||
forAccount: key(item: item),
|
forAccount: key(item: item),
|
||||||
service: Self.keychainServiceName)
|
service: Self.keychainServiceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func item<T: SecretsStorable>(_ item: Item) throws -> T? {
|
func item<T: SecretsStorable>(_ item: Item) throws -> T? {
|
||||||
guard let data = try keychainServiceType.getGenericPassword(
|
guard let data = try keychainService.getGenericPassword(
|
||||||
account: key(item: item),
|
account: key(item: item),
|
||||||
service: Self.keychainServiceName) else {
|
service: Self.keychainServiceName) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -51,20 +65,25 @@ extension SecretsService {
|
||||||
|
|
||||||
func deleteAllItems() throws {
|
func deleteAllItems() throws {
|
||||||
for item in SecretsService.Item.allCases {
|
for item in SecretsService.Item.allCases {
|
||||||
try keychainServiceType.deleteGenericPassword(
|
switch item.kind {
|
||||||
account: key(item: item),
|
case .genericPassword:
|
||||||
service: Self.keychainServiceName)
|
try keychainService.deleteGenericPassword(
|
||||||
|
account: key(item: item),
|
||||||
|
service: Self.keychainServiceName)
|
||||||
|
case .key:
|
||||||
|
try keychainService.deleteKey(applicationTag: key(item: item))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePushKeyAndReturnPublicKey() throws -> Data {
|
func generatePushKeyAndReturnPublicKey() throws -> Data {
|
||||||
try keychainServiceType.generateKeyAndReturnPublicKey(
|
try keychainService.generateKeyAndReturnPublicKey(
|
||||||
applicationTag: key(item: .pushKey),
|
applicationTag: key(item: .pushKey),
|
||||||
attributes: PushKey.attributes)
|
attributes: PushKey.attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPushKey() throws -> Data? {
|
func getPushKey() throws -> Data? {
|
||||||
try keychainServiceType.getPrivateKey(
|
try keychainService.getPrivateKey(
|
||||||
applicationTag: key(item: .pushKey),
|
applicationTag: key(item: .pushKey),
|
||||||
attributes: PushKey.attributes)
|
attributes: PushKey.attributes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ extension RootViewModel {
|
||||||
|
|
||||||
func deleteIdentity(id: UUID) {
|
func deleteIdentity(id: UUID) {
|
||||||
identitiesService.deleteIdentity(id: id)
|
identitiesService.deleteIdentity(id: id)
|
||||||
.sink(receiveCompletion: { _ in }, receiveValue: {})
|
.sink { _ in } receiveValue: { _ in }
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,29 +9,23 @@ class AddIdentityViewModelTests: XCTestCase {
|
||||||
func testAddIdentity() throws {
|
func testAddIdentity() throws {
|
||||||
let identityDatabase = IdentityDatabase.fresh()
|
let identityDatabase = IdentityDatabase.fresh()
|
||||||
let sut = AddIdentityViewModel(identitiesService: .fresh(identityDatabase: identityDatabase))
|
let sut = AddIdentityViewModel(identitiesService: .fresh(identityDatabase: identityDatabase))
|
||||||
let addedIDAndURLRecorder = sut.addedIdentityIDAndURL.record()
|
let addedIDRecorder = sut.addedIdentityID.record()
|
||||||
|
|
||||||
sut.urlFieldText = "https://mastodon.social"
|
sut.urlFieldText = "https://mastodon.social"
|
||||||
sut.logInTapped()
|
sut.logInTapped()
|
||||||
|
|
||||||
let addedIdentityIDAndURL = try wait(for: addedIDAndURLRecorder.next(), timeout: 1)
|
_ = try wait(for: addedIDRecorder.next(), timeout: 1)
|
||||||
|
|
||||||
// XCTAssertEqual(addedIdentityIDAndURL.0, addedIdentityID)
|
|
||||||
XCTAssertEqual(addedIdentityIDAndURL.1, URL(string: "https://mastodon.social")!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAddIdentityWithoutScheme() throws {
|
func testAddIdentityWithoutScheme() throws {
|
||||||
let identityDatabase = IdentityDatabase.fresh()
|
let identityDatabase = IdentityDatabase.fresh()
|
||||||
let sut = AddIdentityViewModel(identitiesService: .fresh(identityDatabase: identityDatabase))
|
let sut = AddIdentityViewModel(identitiesService: .fresh(identityDatabase: identityDatabase))
|
||||||
let addedIDAndURLRecorder = sut.addedIdentityIDAndURL.record()
|
let addedIDRecorder = sut.addedIdentityID.record()
|
||||||
|
|
||||||
sut.urlFieldText = "mastodon.social"
|
sut.urlFieldText = "mastodon.social"
|
||||||
sut.logInTapped()
|
sut.logInTapped()
|
||||||
|
|
||||||
let addedIdentityIDAndURL = try wait(for: addedIDAndURLRecorder.next(), timeout: 1)
|
_ = try wait(for: addedIDRecorder.next(), timeout: 1)
|
||||||
|
|
||||||
// XCTAssertEqual(addedIdentityIDAndURL.0, addedIdentityID)
|
|
||||||
XCTAssertEqual(addedIdentityIDAndURL.1, URL(string: "https://mastodon.social")!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInvalidURL() throws {
|
func testInvalidURL() throws {
|
||||||
|
|
|
@ -20,8 +20,8 @@ class RootViewModelTests: XCTestCase {
|
||||||
|
|
||||||
let addIdentityViewModel = sut.addIdentityViewModel()
|
let addIdentityViewModel = sut.addIdentityViewModel()
|
||||||
|
|
||||||
addIdentityViewModel.addedIdentityIDAndURL
|
addIdentityViewModel.addedIdentityID
|
||||||
.sink(receiveValue: sut.newIdentityCreated(id:instanceURL:))
|
.sink(receiveValue: sut.newIdentitySelected(id:))
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
addIdentityViewModel.urlFieldText = "https://mastodon.social"
|
addIdentityViewModel.urlFieldText = "https://mastodon.social"
|
||||||
|
|
Loading…
Reference in New Issue