Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ let package = Package(
"DeveloperAPI",
"CXKit",
"XUtils",
.product(name: "XADI", package: "xadi", condition: .when(platforms: [.linux])),
.product(name: "XADI", package: "xadi", condition: .when(platforms: [.linux, .macOS])),
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "Dependencies", package: "swift-dependencies"),
.product(name: "SwiftyMobileDevice", package: "SwiftyMobileDevice"),
Expand Down
44 changes: 37 additions & 7 deletions Sources/XKit/GrandSlam/Anisette/ADIDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,10 @@ public struct ADIDataProvider: AnisetteDataProvider {

private let _clientInfo = LockIsolated<String?>(nil)

public init(provisioningData: ProvisioningData? = nil) {
public init() {
@Dependency(\.keyValueStorage) var storage
if let provisioningData {
self.localUserUID = provisioningData.localUserUID
try? storage.setData(provisioningData.adiPb, forKey: Self.provisioningKey)
try? storage.setString("\(provisioningData.routingInfo)", forKey: Self.routingInfoKey)
} else if let localUserUIDString = try? storage.string(forKey: Self.localUserUIDKey),

if let localUserUIDString = try? storage.string(forKey: Self.localUserUIDKey),
let localUserUID = UUID(uuidString: localUserUIDString) {
self.localUserUID = localUserUID
} else {
Expand All @@ -110,9 +107,15 @@ public struct ADIDataProvider: AnisetteDataProvider {
self.localUserID = SHA256.hash(data: Data(localUserUID.uuidString.utf8))
.map { String(format: "%02X", $0) }
.joined()

if (try? storage.string(forKey: Self.providerIDKey)) != Self.providerID {
self.resetProvisioning()
try? storage.setString(Self.providerID, forKey: Self.providerIDKey)
}
}

private static let localUserUIDKey = "XTLLocalUserUID"
private static let providerIDKey = "XTLADIProviderID"
private static let provisioningKey = "XTLProvisioningInfo"
private static let routingInfoKey = "XTLRoutingInfo"

Expand Down Expand Up @@ -245,6 +248,16 @@ public struct ADIDataProvider: AnisetteDataProvider {
try? storage.setString(nil, forKey: Self.routingInfoKey)
}

private static var providerID: String? {
#if os(macOS)
return "1"
#else
return nil
#endif
}

public var providerID: String? { Self.providerID }

public func provisioningData() -> ProvisioningData? {
guard let provisioningInfo = try? storage.data(forKey: Self.provisioningKey),
let routingInfoString = try? storage.string(forKey: Self.routingInfoKey),
Expand Down Expand Up @@ -280,6 +293,23 @@ public struct ADIDataProvider: AnisetteDataProvider {

}

public struct ADIError: Error {
public struct ADIError: Error, CustomStringConvertible {
public var code: Int

public var description: String {
switch code {
case -45061:
return """
You were logged out. Please log in again with `xtool auth`.

If you see this repeatedly, please file an issue at https://github.com/xtool-org/xtool/issues/new/choose
"""
default:
return """
Apple Anisette library returned error \(code). Please try logging in again with `xtool auth`.

If this problem persists, file an issue at https://github.com/xtool-org/xtool/issues/new/choose
"""
}
}
}
9 changes: 9 additions & 0 deletions Sources/XKit/GrandSlam/Anisette/AnisetteDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import Foundation
import Dependencies

public protocol AnisetteDataProvider: Sendable {
/// Stored alongside login data. If the provider reports a new ID, we log out.
///
/// This is useful when switching Anisette provider implementations if the old
/// implementation's provisioning data won't work with the new implementation.
///
/// The default implementation returns `nil`.
var providerID: String? { get }

// This is a suggestion and not a requirement.
func resetProvisioning() async
func provisioningData() -> ProvisioningData?
Expand All @@ -24,6 +32,7 @@ public struct ProvisioningData: Hashable, Codable, Sendable {
}

extension AnisetteDataProvider {
public var providerID: String? { nil }
public func provisioningData() -> ProvisioningData? { nil }
public func resetProvisioning() async {}
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/XToolSupport/AuthCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,15 @@ struct AuthOperation {
}
)

@Dependency(\.anisetteDataProvider) var anisetteProvider

return AuthToken.xcode(.init(
appleID: username,
adsid: token.adsid,
token: token.token,
expiry: token.expiry,
teamID: team.id.rawValue
teamID: team.id.rawValue,
anisetteProviderID: anisetteProvider.providerID
))
}
}
Expand Down
14 changes: 13 additions & 1 deletion Sources/XToolSupport/AuthToken.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum AuthToken: Codable, CustomStringConvertible {
var token: String
var expiry: Date
var teamID: String
var anisetteProviderID: String?
}

struct AppStoreConnect: Codable {
Expand Down Expand Up @@ -58,7 +59,18 @@ extension AuthToken {
guard let data = try storage.data(forKey: "XTLAuthToken") else {
return nil
}
return try decoder.decode(AuthToken.self, from: data)
let decoded = try decoder.decode(AuthToken.self, from: data)
switch decoded {
case .xcode(let xcode):
@Dependency(\.anisetteDataProvider) var anisetteProvider
guard xcode.anisetteProviderID == anisetteProvider.providerID else {
try? clear()
return nil
}
case .appStoreConnect:
break
}
return decoded
}

static func clear() throws {
Expand Down
2 changes: 2 additions & 0 deletions macOS/Support/XToolMac.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)sh.xtool.keychain</string>
Expand Down
Loading