Refactoring

This commit is contained in:
Justin Mazzocchi 2020-08-08 19:52:41 -07:00
parent 6f487524dd
commit 6dfda031a6
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
9 changed files with 66 additions and 46 deletions

View File

@ -10,19 +10,18 @@ private let devInstanceURL = URL(string: "https://mastodon.social")!
private let devIdentityID = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")! private let devIdentityID = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
private let devAccessToken = "DEVELOPMENT_ACCESS_TOKEN" private let devAccessToken = "DEVELOPMENT_ACCESS_TOKEN"
extension Secrets { func freshKeychainService() -> KeychainServiceType { MockKeychainService() }
static func fresh() -> Secrets { Secrets(keychainService: MockKeychainService()) }
static let development: Secrets = { let developmentKeychainService: KeychainServiceType = {
let secrets = Secrets.fresh() let keychainService = MockKeychainService()
let secretsService = SecretsService(identityID: devIdentityID, keychainService: keychainService)
try! secrets.set("DEVELOPMENT_CLIENT_ID", forItem: .clientID, forIdentityID: devIdentityID) try! secretsService.set("DEVELOPMENT_CLIENT_ID", forItem: .clientID)
try! secrets.set("DEVELOPMENT_CLIENT_SECRET", forItem: .clientSecret, forIdentityID: devIdentityID) try! secretsService.set("DEVELOPMENT_CLIENT_SECRET", forItem: .clientSecret)
try! secrets.set(devAccessToken, forItem: .accessToken, forIdentityID: devIdentityID) try! secretsService.set(devAccessToken, forItem: .accessToken)
return secrets return keychainService
}() }()
}
extension Defaults { extension Defaults {
static func fresh() -> Defaults { Defaults(userDefaults: MockUserDefaults()) } static func fresh() -> Defaults { Defaults(userDefaults: MockUserDefaults()) }
@ -74,13 +73,13 @@ extension AppEnvironment {
URLSessionConfiguration: URLSessionConfiguration = .stubbing, URLSessionConfiguration: URLSessionConfiguration = .stubbing,
identityDatabase: IdentityDatabase = .fresh(), identityDatabase: IdentityDatabase = .fresh(),
defaults: Defaults = .fresh(), defaults: Defaults = .fresh(),
secrets: Secrets = .fresh(), keychainService: KeychainServiceType = freshKeychainService(),
webAuthSessionType: WebAuthSessionType.Type = SuccessfulMockWebAuthSession.self) -> AppEnvironment { webAuthSessionType: WebAuthSessionType.Type = SuccessfulMockWebAuthSession.self) -> AppEnvironment {
AppEnvironment( AppEnvironment(
URLSessionConfiguration: URLSessionConfiguration, URLSessionConfiguration: URLSessionConfiguration,
identityDatabase: identityDatabase, identityDatabase: identityDatabase,
defaults: defaults, defaults: defaults,
secrets: secrets, keychainService: keychainService,
webAuthSessionType: webAuthSessionType) webAuthSessionType: webAuthSessionType)
} }
@ -88,7 +87,7 @@ extension AppEnvironment {
URLSessionConfiguration: .stubbing, URLSessionConfiguration: .stubbing,
identityDatabase: .development, identityDatabase: .development,
defaults: .development, defaults: .development,
secrets: .development, keychainService: developmentKeychainService,
webAuthSessionType: SuccessfulMockWebAuthSession.self) webAuthSessionType: SuccessfulMockWebAuthSession.self)
} }

View File

@ -77,8 +77,6 @@
D0666A6424C6DC6C00F3F04B /* AppAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A6224C6DC6C00F3F04B /* AppAuthorization.swift */; }; D0666A6424C6DC6C00F3F04B /* AppAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A6224C6DC6C00F3F04B /* AppAuthorization.swift */; };
D0666A6F24C6DFB300F3F04B /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A6E24C6DFB300F3F04B /* AccessToken.swift */; }; D0666A6F24C6DFB300F3F04B /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A6E24C6DFB300F3F04B /* AccessToken.swift */; };
D0666A7024C6DFB300F3F04B /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A6E24C6DFB300F3F04B /* AccessToken.swift */; }; D0666A7024C6DFB300F3F04B /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A6E24C6DFB300F3F04B /* AccessToken.swift */; };
D0666A7224C6E0D300F3F04B /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A7124C6E0D300F3F04B /* Secrets.swift */; };
D0666A7324C6E0D300F3F04B /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0666A7124C6E0D300F3F04B /* Secrets.swift */; };
D0666A7D24C7745A00F3F04B /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = D0666A7C24C7745A00F3F04B /* GRDB */; }; D0666A7D24C7745A00F3F04B /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = D0666A7C24C7745A00F3F04B /* GRDB */; };
D06B491F24D3F7FE00642749 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D06B491E24D3F7FE00642749 /* Localizable.strings */; }; D06B491F24D3F7FE00642749 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D06B491E24D3F7FE00642749 /* Localizable.strings */; };
D06B492024D3FB8000642749 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D06B491E24D3F7FE00642749 /* Localizable.strings */; }; D06B492024D3FB8000642749 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D06B491E24D3F7FE00642749 /* Localizable.strings */; };
@ -140,6 +138,8 @@
D0EC8DC324DF7D9C00A08489 /* IdentityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */; }; D0EC8DC324DF7D9C00A08489 /* IdentityService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */; };
D0EC8DC524DF842700A08489 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; }; D0EC8DC524DF842700A08489 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; };
D0EC8DC624DF842700A08489 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; }; D0EC8DC624DF842700A08489 /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC424DF842700A08489 /* KeychainService.swift */; };
D0EC8DC824DF8B3C00A08489 /* SecretsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */; };
D0EC8DC924DF8B3C00A08489 /* SecretsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */; };
D0ED1B6E24CE100C00B4899C /* AddIdentityViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1B6D24CE100C00B4899C /* AddIdentityViewModelTests.swift */; }; D0ED1B6E24CE100C00B4899C /* AddIdentityViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1B6D24CE100C00B4899C /* AddIdentityViewModelTests.swift */; };
D0ED1BB724CE47F400B4899C /* WebAuthSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1BB624CE47F400B4899C /* WebAuthSession.swift */; }; D0ED1BB724CE47F400B4899C /* WebAuthSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1BB624CE47F400B4899C /* WebAuthSession.swift */; };
D0ED1BB824CE47F400B4899C /* WebAuthSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1BB624CE47F400B4899C /* WebAuthSession.swift */; }; D0ED1BB824CE47F400B4899C /* WebAuthSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ED1BB624CE47F400B4899C /* WebAuthSession.swift */; };
@ -216,7 +216,6 @@
D0666A5324C6C3E500F3F04B /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; }; D0666A5324C6C3E500F3F04B /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; };
D0666A6224C6DC6C00F3F04B /* AppAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAuthorization.swift; sourceTree = "<group>"; }; D0666A6224C6DC6C00F3F04B /* AppAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAuthorization.swift; sourceTree = "<group>"; };
D0666A6E24C6DFB300F3F04B /* AccessToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessToken.swift; sourceTree = "<group>"; }; D0666A6E24C6DFB300F3F04B /* AccessToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessToken.swift; sourceTree = "<group>"; };
D0666A7124C6E0D300F3F04B /* Secrets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Secrets.swift; sourceTree = "<group>"; };
D06B491E24D3F7FE00642749 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; }; D06B491E24D3F7FE00642749 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
D074577624D29006004758DB /* MockWebAuthSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockWebAuthSession.swift; sourceTree = "<group>"; }; D074577624D29006004758DB /* MockWebAuthSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockWebAuthSession.swift; sourceTree = "<group>"; };
D074577924D29366004758DB /* URLSessionConfiguration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+Extensions.swift"; sourceTree = "<group>"; }; D074577924D29366004758DB /* URLSessionConfiguration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+Extensions.swift"; sourceTree = "<group>"; };
@ -245,6 +244,7 @@
D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockKeychainService.swift; sourceTree = "<group>"; }; D0DC177624D0CF2600A75C65 /* MockKeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockKeychainService.swift; sourceTree = "<group>"; };
D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentityService.swift; sourceTree = "<group>"; }; D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentityService.swift; sourceTree = "<group>"; };
D0EC8DC424DF842700A08489 /* KeychainService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = "<group>"; }; D0EC8DC424DF842700A08489 /* KeychainService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = "<group>"; };
D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretsService.swift; sourceTree = "<group>"; };
D0ED1B6D24CE100C00B4899C /* AddIdentityViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityViewModelTests.swift; sourceTree = "<group>"; }; D0ED1B6D24CE100C00B4899C /* AddIdentityViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentityViewModelTests.swift; sourceTree = "<group>"; };
D0ED1BB624CE47F400B4899C /* WebAuthSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebAuthSession.swift; sourceTree = "<group>"; }; D0ED1BB624CE47F400B4899C /* WebAuthSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebAuthSession.swift; sourceTree = "<group>"; };
D0ED1BC024CED48800B4899C /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = "<group>"; }; D0ED1BC024CED48800B4899C /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = "<group>"; };
@ -351,6 +351,7 @@
children = ( children = (
D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */, D0EC8DC124DF7D9C00A08489 /* IdentityService.swift */,
D0EC8DC424DF842700A08489 /* KeychainService.swift */, D0EC8DC424DF842700A08489 /* KeychainService.swift */,
D0EC8DC724DF8B3C00A08489 /* SecretsService.swift */,
); );
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
@ -440,7 +441,6 @@
D0666A4D24C6C39600F3F04B /* Instance.swift */, D0666A4D24C6C39600F3F04B /* Instance.swift */,
D0ED1BE224CFA84400B4899C /* MastodonError.swift */, D0ED1BE224CFA84400B4899C /* MastodonError.swift */,
D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */, D0CD847224DBDEC700CF380C /* MastodonPreferences.swift */,
D0666A7124C6E0D300F3F04B /* Secrets.swift */,
D0CD847524DBDF3C00CF380C /* Status.swift */, D0CD847524DBDF3C00CF380C /* Status.swift */,
D0CD847B24DBEA9F00CF380C /* Unknowable.swift */, D0CD847B24DBEA9F00CF380C /* Unknowable.swift */,
); );
@ -808,10 +808,10 @@
D0ED1BCE24CF768200B4899C /* MastodonEndpoint.swift in Sources */, D0ED1BCE24CF768200B4899C /* MastodonEndpoint.swift in Sources */,
D074577A24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */, D074577A24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */,
D0ED1BB724CE47F400B4899C /* WebAuthSession.swift in Sources */, D0ED1BB724CE47F400B4899C /* WebAuthSession.swift in Sources */,
D0666A7224C6E0D300F3F04B /* Secrets.swift in Sources */,
D0A1CA7424DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */, D0A1CA7424DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */,
D0159F9124DE743700E78478 /* TabNavigationView.swift in Sources */, D0159F9124DE743700E78478 /* TabNavigationView.swift in Sources */,
D0ED1BC424CED54D00B4899C /* HTTPTarget.swift in Sources */, D0ED1BC424CED54D00B4899C /* HTTPTarget.swift in Sources */,
D0EC8DC824DF8B3C00A08489 /* SecretsService.swift in Sources */,
D0159FA324DE955900E78478 /* CustomEmojiText.swift in Sources */, D0159FA324DE955900E78478 /* CustomEmojiText.swift in Sources */,
D0C963FE24CC3812003BD330 /* Publisher+Extensions.swift in Sources */, D0C963FE24CC3812003BD330 /* Publisher+Extensions.swift in Sources */,
D04FD73C24D4A83A007D572D /* InstanceEndpoint+Stubbing.swift in Sources */, D04FD73C24D4A83A007D572D /* InstanceEndpoint+Stubbing.swift in Sources */,
@ -872,6 +872,7 @@
D0DC174E24CFF1F100A75C65 /* Stubbing.swift in Sources */, D0DC174E24CFF1F100A75C65 /* Stubbing.swift in Sources */,
D0091B6C24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift in Sources */, D0091B6C24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift in Sources */,
D0091B6F24DD68090040E8D2 /* PreferencesView.swift in Sources */, D0091B6F24DD68090040E8D2 /* PreferencesView.swift in Sources */,
D0EC8DC924DF8B3C00A08489 /* SecretsService.swift in Sources */,
D0DB6EF524C5233E00D965FE /* AddIdentityView.swift in Sources */, D0DB6EF524C5233E00D965FE /* AddIdentityView.swift in Sources */,
D019E6EA24DF72E700697C7D /* InstanceEndpoint.swift in Sources */, D019E6EA24DF72E700697C7D /* InstanceEndpoint.swift in Sources */,
D0159F9C24DE748C00E78478 /* SidebarNavigationView.swift in Sources */, D0159F9C24DE748C00E78478 /* SidebarNavigationView.swift in Sources */,
@ -880,7 +881,6 @@
D0ED1BCF24CF768200B4899C /* MastodonEndpoint.swift in Sources */, D0ED1BCF24CF768200B4899C /* MastodonEndpoint.swift in Sources */,
D074577B24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */, D074577B24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */,
D0ED1BB824CE47F400B4899C /* WebAuthSession.swift in Sources */, D0ED1BB824CE47F400B4899C /* WebAuthSession.swift in Sources */,
D0666A7324C6E0D300F3F04B /* Secrets.swift in Sources */,
D0A1CA7524DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */, D0A1CA7524DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */,
D0ED1BC524CED54D00B4899C /* HTTPTarget.swift in Sources */, D0ED1BC524CED54D00B4899C /* HTTPTarget.swift in Sources */,
D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */, D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */,

View File

@ -19,7 +19,7 @@ struct MetatextApp: App {
URLSessionConfiguration: .default, URLSessionConfiguration: .default,
identityDatabase: identityDatabase, identityDatabase: identityDatabase,
defaults: Defaults(userDefaults: .standard), defaults: Defaults(userDefaults: .standard),
secrets: Secrets(keychainService: KeychainService(serviceName: "com.metabolist.metatext")), keychainService: KeychainService(serviceName: Self.keychainServiceName),
webAuthSessionType: WebAuthSession.self) webAuthSessionType: WebAuthSession.self)
} }
@ -29,3 +29,7 @@ struct MetatextApp: App {
} }
} }
} }
private extension MetatextApp {
static let keychainServiceName = "com.metabolist.metatext"
}

View File

@ -6,6 +6,6 @@ struct AppEnvironment {
let URLSessionConfiguration: URLSessionConfiguration let URLSessionConfiguration: URLSessionConfiguration
let identityDatabase: IdentityDatabase let identityDatabase: IdentityDatabase
let defaults: Defaults let defaults: Defaults
let secrets: Secrets let keychainService: KeychainServiceType
let webAuthSessionType: WebAuthSessionType.Type let webAuthSessionType: WebAuthSessionType.Type
} }

View File

@ -16,7 +16,10 @@ class IdentityService {
self.appEnvironment = appEnvironment self.appEnvironment = appEnvironment
observationErrors = observationErrorsInput.eraseToAnyPublisher() observationErrors = observationErrorsInput.eraseToAnyPublisher()
networkClient = MastodonClient(configuration: appEnvironment.URLSessionConfiguration) networkClient = MastodonClient(configuration: appEnvironment.URLSessionConfiguration)
networkClient.accessToken = try appEnvironment.secrets.item(.accessToken, forIdentityID: identityID) networkClient.accessToken = try SecretsService(
identityID: identityID,
keychainService: appEnvironment.keychainService)
.item(.accessToken)
let observation = appEnvironment.identityDatabase.identityObservation(id: identityID).share() let observation = appEnvironment.identityDatabase.identityObservation(id: identityID).share()

View File

@ -11,42 +11,46 @@ enum SecretsStorableError: Error {
case conversionFromDataStoredInSecrets(Data) case conversionFromDataStoredInSecrets(Data)
} }
class Secrets { struct SecretsService {
let identityID: UUID
private let keychainService: KeychainServiceType private let keychainService: KeychainServiceType
init(keychainService: KeychainServiceType) { init(identityID: UUID, keychainService: KeychainServiceType) {
self.identityID = identityID
self.keychainService = keychainService self.keychainService = keychainService
} }
} }
extension Secrets { extension SecretsService {
enum Item: String { enum Item: String, CaseIterable {
case clientID = "client-id" case clientID = "client-id"
case clientSecret = "client-secret" case clientSecret = "client-secret"
case accessToken = "access-token" case accessToken = "access-token"
} }
} }
extension Secrets { extension SecretsService {
func set(_ data: SecretsStorable, forItem item: Item, forIdentityID identityID: UUID) throws { func set(_ data: SecretsStorable, forItem item: Item) throws {
try keychainService.set(data: data.dataStoredInSecrets, forKey: Self.key(item: item, identityID: identityID)) try keychainService.set(data: data.dataStoredInSecrets, forKey: key(item: item))
} }
func item<T: SecretsStorable>(_ item: Item, forIdentityID identityID: UUID) throws -> T? { func item<T: SecretsStorable>(_ item: Item) throws -> T? {
guard let data = try keychainService.getData(key: Self.key(item: item, identityID: identityID)) else { guard let data = try keychainService.getData(key: key(item: item)) else {
return nil return nil
} }
return try T.fromDataStoredInSecrets(data) return try T.fromDataStoredInSecrets(data)
} }
func delete(_ item: Item, forIdentityID identityID: UUID) throws { func deleteAllItems() throws {
try keychainService.deleteData(key: Self.key(item: item, identityID: identityID)) for item in SecretsService.Item.allCases {
try keychainService.deleteData(key: key(item: item))
}
} }
} }
private extension Secrets { private extension SecretsService {
static func key(item: Item, identityID: UUID) -> String { func key(item: Item) -> String {
identityID.uuidString + "." + item.rawValue identityID.uuidString + "." + item.rawValue
} }
} }

View File

@ -39,7 +39,7 @@ class AddIdentityViewModel: ObservableObject {
identityID: identityID, identityID: identityID,
instanceURL: instanceURL, instanceURL: instanceURL,
redirectURL: redirectURL, redirectURL: redirectURL,
secrets: environment.secrets) keychainService: environment.keychainService)
.authenticationURL(instanceURL: instanceURL, redirectURL: redirectURL) .authenticationURL(instanceURL: instanceURL, redirectURL: redirectURL)
.authenticate( .authenticate(
webAuthSessionType: environment.webAuthSessionType, webAuthSessionType: environment.webAuthSessionType,
@ -67,7 +67,7 @@ private extension AddIdentityViewModel {
identityID: UUID, identityID: UUID,
instanceURL: URL, instanceURL: URL,
redirectURL: URL, redirectURL: URL,
secrets: Secrets) -> AnyPublisher<AppAuthorization, Error> { keychainService: KeychainServiceType) -> AnyPublisher<AppAuthorization, Error> {
let endpoint = AppAuthorizationEndpoint.apps( let endpoint = AppAuthorizationEndpoint.apps(
clientName: MastodonAPI.OAuth.clientName, clientName: MastodonAPI.OAuth.clientName,
redirectURI: redirectURL.absoluteString, redirectURI: redirectURL.absoluteString,
@ -77,8 +77,9 @@ private extension AddIdentityViewModel {
return networkClient.request(target) return networkClient.request(target)
.tryMap { .tryMap {
try secrets.set($0.clientId, forItem: .clientID, forIdentityID: identityID) let secretsService = SecretsService(identityID: identityID, keychainService: keychainService)
try secrets.set($0.clientSecret, forItem: .clientSecret, forIdentityID: identityID) try secretsService.set($0.clientId, forItem: .clientID)
try secretsService.set($0.clientSecret, forItem: .clientSecret)
return $0 return $0
} }
@ -174,7 +175,9 @@ private extension Publisher where Output == (AppAuthorization, String), Failure
private extension Publisher where Output == AccessToken { private extension Publisher where Output == AccessToken {
func createIdentity(id: UUID, instanceURL: URL, environment: AppEnvironment) -> AnyPublisher<UUID, Error> { func createIdentity(id: UUID, instanceURL: URL, environment: AppEnvironment) -> AnyPublisher<UUID, Error> {
tryMap { accessToken -> (UUID, URL) in tryMap { accessToken -> (UUID, URL) in
try environment.secrets.set(accessToken.accessToken, forItem: .accessToken, forIdentityID: id) let secretsService = SecretsService(identityID: id, keychainService: environment.keychainService)
try secretsService.set(accessToken.accessToken, forItem: .accessToken)
return (id, instanceURL) return (id, instanceURL)
} }

View File

@ -26,6 +26,13 @@ extension RootViewModel {
func deleteIdentity(id: UUID) { func deleteIdentity(id: UUID) {
environment.identityDatabase.deleteIdentity(id: id) environment.identityDatabase.deleteIdentity(id: id)
.continuingIfWeakReferenceIsStillAlive(to: self)
.tryMap {
try SecretsService(
identityID: id,
keychainService: $1.environment.keychainService)
.deleteAllItems()
}
.sink(receiveCompletion: { _ in }, receiveValue: {}) .sink(receiveCompletion: { _ in }, receiveValue: {})
.store(in: &cancellables) .store(in: &cancellables)
} }

View File

@ -20,15 +20,15 @@ class AddIdentityViewModelTests: XCTestCase {
XCTAssertEqual(addedIdentity.id, addedIdentityID) XCTAssertEqual(addedIdentity.id, addedIdentityID)
XCTAssertEqual(addedIdentity.url, URL(string: "https://mastodon.social")!) XCTAssertEqual(addedIdentity.url, URL(string: "https://mastodon.social")!)
let secretsService = SecretsService(identityID: addedIdentity.id, keychainService: environment.keychainService)
XCTAssertEqual( XCTAssertEqual(
try environment.secrets.item(.clientID, forIdentityID: addedIdentityID) as String?, try secretsService.item(.clientID) as String?, "AUTHORIZATION_CLIENT_ID_STUB_VALUE")
"AUTHORIZATION_CLIENT_ID_STUB_VALUE")
XCTAssertEqual( XCTAssertEqual(
try environment.secrets.item(.clientSecret, forIdentityID: addedIdentityID) as String?, try secretsService.item(.clientSecret) as String?, "AUTHORIZATION_CLIENT_SECRET_STUB_VALUE")
"AUTHORIZATION_CLIENT_SECRET_STUB_VALUE")
XCTAssertEqual( XCTAssertEqual(
try environment.secrets.item(.accessToken, forIdentityID: addedIdentityID) as String?, try secretsService.item(.accessToken) as String?, "ACCESS_TOKEN_STUB_VALUE")
"ACCESS_TOKEN_STUB_VALUE")
} }
func testAddIdentityWithoutScheme() throws { func testAddIdentityWithoutScheme() throws {