Better handling of when AppleId is not a developer

This commit is contained in:
Matt Kiazyk 2021-10-14 15:43:16 -05:00
parent 04c79c36ad
commit 1496f32e28
No known key found for this signature in database
GPG key ID: 33D9938D5D45EFE2
4 changed files with 49 additions and 12 deletions

View file

@ -148,11 +148,12 @@ public class Client {
/// Use the olympus session endpoint to see if the existing session is still valid
public func validateSession() -> AnyPublisher<Void, Error> {
return Current.network.dataTask(with: URLRequest.olympusSession)
.tryMap { (data, response) in
guard
let jsonObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any],
jsonObject["provider"] != nil
else { throw AuthenticationError.invalidSession }
.map(\.data)
.decode(type: AppleSession.self, decoder: JSONDecoder())
.tryMap { session in
if session.provider == nil {
throw AuthenticationError.notDeveloperAppleId
}
}
.eraseToAnyPublisher()
}
@ -174,6 +175,7 @@ public enum AuthenticationState: Equatable {
case unauthenticated
case waitingForSecondFactor(TwoFactorOption, AuthOptionsResponse, AppleSessionData)
case authenticated
case notAppleDeveloper
}
public enum AuthenticationError: Swift.Error, LocalizedError, Equatable {
@ -186,7 +188,8 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable {
case accountUsesUnknownAuthenticationKind(String?)
case accountLocked(String)
case badStatusCode(statusCode: Int, data: Data, response: HTTPURLResponse)
case notDeveloperAppleId
public var errorDescription: String? {
switch self {
case .invalidSession:
@ -212,6 +215,8 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable {
return message
case let .badStatusCode(statusCode, _, _):
return "Received an unexpected status code: \(statusCode). If you continue to have problems, please submit a bug report in the Help menu."
case .notDeveloperAppleId:
return "You are not registered as an Apple Developer. Please visit Apple Developer Registration. https://developer.apple.com/register/"
}
}
}
@ -362,3 +367,20 @@ public enum SecurityCode {
}
}
}
/// Object returned from olympus/v1/session
/// Used to check Provider, and show name
/// If Provider is nil, we can assume the Apple User is NOT an Apple Developer and can't download Xcode.
public struct AppleSession: Decodable, Equatable {
public let user: AppleUser
public let provider: AppleProvider?
}
public struct AppleProvider: Decodable, Equatable {
public let providerId: Int
public let name: String
}
public struct AppleUser: Decodable, Equatable {
public let fullName: String
}

View file

@ -40,9 +40,15 @@ extension AppState {
}
private func install(_ installationType: InstallationType, downloader: Downloader, attemptNumber: Int) -> AnyPublisher<InstalledXcode, Error> {
// We need to check if the Apple ID that is logged in is an Apple Developer
// Since users can use xcodereleases, we don't check for Apple ID on a xcode list refresh
// If the Apple Id is not a developer, the download action will try and download a xip that is invalid, causing a `xcode13.0.xip is damaged and can't be expanded.`
Logger.appState.info("Using \(downloader) downloader")
return getXcodeArchive(installationType, downloader: downloader)
return validateSession()
.flatMap { _ in
self.getXcodeArchive(installationType, downloader: downloader)
}
.flatMap { xcode, url -> AnyPublisher<InstalledXcode, Swift.Error> in
self.installArchivedXcode(xcode, at: url)
}

View file

@ -71,7 +71,12 @@ extension AppState {
private func updateAvailableXcodes(from dataSource: DataSource) -> AnyPublisher<[AvailableXcode], Error> {
switch dataSource {
case .apple:
return signInIfNeeded()
return signInIfNeeded()
.flatMap { [unowned self] in
// this will check to see if the Apple ID is a valid Apple Developer or not.
// If it's not, we can't use the Apple source to get xcode info.
self.validateSession()
}
.flatMap { [unowned self] in self.releasedXcodes().combineLatest(self.prereleaseXcodes()) }
.receive(on: DispatchQueue.main)
.map { releasedXcodes, prereleaseXcodes in

View file

@ -116,8 +116,9 @@ class AppState: ObservableObject {
.receive(on: DispatchQueue.main)
.handleEvents(receiveCompletion: { completion in
if case .failure = completion {
self.authenticationState = .unauthenticated
self.presentedSheet = .signIn
// this is causing some awkwardness with showing an alert with the error and also popping up the sign in view
// self.authenticationState = .unauthenticated
// self.presentedSheet = .signIn
}
})
.eraseToAnyPublisher()
@ -227,7 +228,7 @@ class AppState: ObservableObject {
self.authError = error
case .finished:
switch self.authenticationState {
case .authenticated, .unauthenticated:
case .authenticated, .unauthenticated, .notAppleDeveloper:
self.presentedSheet = nil
self.secondFactorData = nil
case let .waitingForSecondFactor(option, authOptions, sessionData):
@ -315,7 +316,7 @@ class AppState: ObservableObject {
self.$authenticationState
.filter { state in
switch state {
case .authenticated, .unauthenticated: return true
case .authenticated, .unauthenticated, .notAppleDeveloper: return true
case .waitingForSecondFactor: return false
}
}
@ -324,6 +325,9 @@ class AppState: ObservableObject {
if state == .unauthenticated {
throw AuthenticationError.invalidSession
}
if state == .notAppleDeveloper {
throw AuthenticationError.notDeveloperAppleId
}
return Void()
}
}