mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Merge pull request #159 from RobotsAndPencils/matt/NonDeveloper
Better handling of when AppleId is not a developer
This commit is contained in:
commit
83fa00b21f
7 changed files with 65 additions and 15 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import os.log
|
|||
|
||||
class AppState: ObservableObject {
|
||||
private let client = AppleAPI.Client()
|
||||
|
||||
|
||||
// MARK: - Published Properties
|
||||
|
||||
@Published var authenticationState: AuthenticationState = .unauthenticated
|
||||
|
|
@ -112,12 +112,13 @@ class AppState: ObservableObject {
|
|||
// MARK: - Authentication
|
||||
|
||||
func validateSession() -> AnyPublisher<Void, Error> {
|
||||
return client.validateSession()
|
||||
return Current.network.validateSession()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ private func _installedXcodes(destination: Path) -> [InstalledXcode] {
|
|||
public struct Network {
|
||||
private static let client = AppleAPI.Client()
|
||||
|
||||
public var dataTask: (URLRequest) -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> = {
|
||||
public var dataTask: (URLRequest) -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> = {
|
||||
AppleAPI.Current.network.session.dataTaskPublisher(for: $0)
|
||||
.mapError { $0 as Error }
|
||||
.eraseToAnyPublisher()
|
||||
|
|
@ -183,6 +183,10 @@ public struct Network {
|
|||
public func downloadTask(with url: URL, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) {
|
||||
return downloadTask(url, saveLocation, resumeData)
|
||||
}
|
||||
|
||||
public var validateSession: () -> AnyPublisher<Void, Error> = {
|
||||
return client.validateSession()
|
||||
}
|
||||
}
|
||||
|
||||
public struct Keychain {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ class AppStateTests: XCTestCase {
|
|||
return true
|
||||
}
|
||||
}
|
||||
Xcodes.Current.network.validateSession = {
|
||||
return Just(())
|
||||
.setFailureType(to: Error.self).eraseToAnyPublisher()
|
||||
}
|
||||
Xcodes.Current.network.dataTask = { urlRequest in
|
||||
// Don't have a valid session
|
||||
if urlRequest.url! == URLRequest.olympusSession.url! {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ extension Network {
|
|||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
)
|
||||
},
|
||||
validateSession: {
|
||||
return Just(())
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue