Refreshes apps by installing provisioning profiles when possible

Assuming the certificate used to originally sign an app is still valid, we can refresh an app simply by installing new provisioning profiles. However, if the signing certificate is no longer valid, we fall back to the old method of resigning + reinstalling.
This commit is contained in:
Riley Testut
2020-03-06 17:08:35 -08:00
parent 27bce4e456
commit 4f00018164
25 changed files with 1272 additions and 1082 deletions

View File

@@ -32,9 +32,9 @@ enum AuthenticationError: LocalizedError
}
@objc(AuthenticationOperation)
class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, ALTAppleAPISession)>
{
let group: OperationGroup
let context: AuthenticatedOperationContext
private weak var presentingViewController: UIViewController?
@@ -56,22 +56,23 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
private var submitCodeAction: UIAlertAction?
init(group: OperationGroup, presentingViewController: UIViewController?)
init(context: AuthenticatedOperationContext, presentingViewController: UIViewController?)
{
self.group = group
self.context = context
self.presentingViewController = presentingViewController
super.init()
self.context.authenticationOperation = self
self.operationQueue.name = "com.altstore.AuthenticationOperation"
self.progress.totalUnitCount = 3
self.progress.totalUnitCount = 4
}
override func main()
{
super.main()
if let error = self.group.error
if let error = self.context.error
{
self.finish(.failure(error))
return
@@ -85,6 +86,7 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
{
case .failure(let error): self.finish(.failure(error))
case .success(let account, let session):
self.context.session = session
self.progress.completedUnitCount += 1
// Fetch Team
@@ -95,6 +97,7 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
{
case .failure(let error): self.finish(.failure(error))
case .success(let team):
self.context.team = team
self.progress.completedUnitCount += 1
// Fetch Certificate
@@ -105,22 +108,33 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
{
case .failure(let error): self.finish(.failure(error))
case .success(let certificate):
self.context.certificate = certificate
self.progress.completedUnitCount += 1
// Save account/team to disk.
self.save(team) { (result) in
// Register Device
self.registerCurrentDevice(for: team, session: session) { (result) in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
{
case .failure(let error): self.finish(.failure(error))
case .success:
let signer = ALTSigner(team: team, certificate: certificate)
self.progress.completedUnitCount += 1
// Must cache App IDs _after_ saving account/team to disk.
self.cacheAppIDs(signer: signer, session: session) { (result) in
let result = result.map { _ in (signer, session) }
self.finish(result)
// Save account/team to disk.
self.save(team) { (result) in
guard !self.isCancelled else { return self.finish(.failure(OperationError.cancelled)) }
switch result
{
case .failure(let error): self.finish(.failure(error))
case .success:
// Must cache App IDs _after_ saving account/team to disk.
self.cacheAppIDs(team: team, session: session) { (result) in
let result = result.map { _ in (team, certificate, session) }
self.finish(result)
}
}
}
}
}
@@ -173,21 +187,21 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
}
}
override func finish(_ result: Result<(ALTSigner, ALTAppleAPISession), Error>)
override func finish(_ result: Result<(ALTTeam, ALTCertificate, ALTAppleAPISession), Error>)
{
guard !self.isFinished else { return }
print("Finished authenticating with result:", result)
print("Finished authenticating with result:", result.error?.localizedDescription ?? "success")
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
context.perform {
do
{
let (signer, session) = try result.get()
let (altTeam, altCertificate, session) = try result.get()
guard
let account = Account.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Account.identifier), signer.team.account.identifier), in: context),
let team = Team.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Team.identifier), signer.team.identifier), in: context)
let account = Account.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Account.identifier), altTeam.account.identifier), in: context),
let team = Team.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(Team.identifier), altTeam.identifier), in: context)
else { throw AuthenticationError.noTeam }
// Account
@@ -214,24 +228,19 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
team.isActiveTeam = false
}
if let altStoreApp = InstalledApp.fetchAltStore(in: context), altStoreApp.team == nil
{
// No team assigned to AltStore app yet, so assume this team was used to originally install it.
altStoreApp.team = team
}
// Save
try context.save()
// Update keychain
Keychain.shared.appleIDEmailAddress = signer.team.account.appleID
Keychain.shared.appleIDEmailAddress = altTeam.account.appleID
Keychain.shared.appleIDPassword = self.appleIDPassword
Keychain.shared.signingCertificate = signer.certificate.p12Data()
Keychain.shared.signingCertificatePassword = signer.certificate.machineIdentifier
Keychain.shared.signingCertificate = altCertificate.p12Data()
Keychain.shared.signingCertificatePassword = altCertificate.machineIdentifier
self.showInstructionsIfNecessary() { (didShowInstructions) in
let signer = ALTSigner(team: altTeam, certificate: altCertificate)
// Refresh screen must go last since a successful refresh will cause the app to quit.
self.showRefreshScreenIfNecessary(signer: signer, session: session) { (didShowRefreshAlert) in
super.finish(result)
@@ -340,7 +349,7 @@ private extension AuthenticationOperation
func authenticate(appleID: String, password: String, completionHandler: @escaping (Result<(ALTAccount, ALTAppleAPISession), Swift.Error>) -> Void)
{
let fetchAnisetteDataOperation = FetchAnisetteDataOperation(group: self.group)
let fetchAnisetteDataOperation = FetchAnisetteDataOperation(context: self.context)
fetchAnisetteDataOperation.resultHandler = { (result) in
switch result
{
@@ -557,13 +566,38 @@ private extension AuthenticationOperation
}
}
func cacheAppIDs(signer: ALTSigner, session: ALTAppleAPISession, completionHandler: @escaping (Result<Void, Error>) -> Void)
func registerCurrentDevice(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void)
{
let group = OperationGroup()
group.signer = signer
group.session = session
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else {
return completionHandler(.failure(OperationError.unknownUDID))
}
let fetchAppIDsOperation = FetchAppIDsOperation(group: group)
ALTAppleAPI.shared.fetchDevices(for: team, session: session) { (devices, error) in
do
{
let devices = try Result(devices, error).get()
if let device = devices.first(where: { $0.identifier == udid })
{
completionHandler(.success(device))
}
else
{
ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, team: team, session: session) { (device, error) in
completionHandler(Result(device, error))
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
func cacheAppIDs(team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<Void, Error>) -> Void)
{
let fetchAppIDsOperation = FetchAppIDsOperation(context: self.context)
fetchAppIDsOperation.resultHandler = { (result) in
do
{
@@ -611,8 +645,7 @@ private extension AuthenticationOperation
#else
DispatchQueue.main.async {
let refreshViewController = self.storyboard.instantiateViewController(withIdentifier: "refreshAltStoreViewController") as! RefreshAltStoreViewController
refreshViewController.signer = signer
refreshViewController.session = session
refreshViewController.context = self.context
refreshViewController.completionHandler = { _ in
completionHandler(true)
}