2019-06-05 18:05:21 -07:00
//
// A u t h e n t i c a t i o n O p e r a t i o n . s w i f t
// A l t S t o r e
//
// C r e a t e d b y R i l e y T e s t u t o n 6 / 5 / 1 9 .
// C o p y r i g h t © 2 0 1 9 R i l e y T e s t u t . A l l r i g h t s r e s e r v e d .
//
import Foundation
import Roxas
2019-11-18 14:49:17 -08:00
import Network
2019-06-05 18:05:21 -07:00
2020-09-03 16:39:08 -07:00
import AltStoreCore
2019-06-05 18:05:21 -07:00
import AltSign
2019-06-10 15:03:47 -07:00
enum AuthenticationError : LocalizedError
2019-06-05 18:05:21 -07:00
{
2019-06-10 15:03:47 -07:00
case noTeam
case noCertificate
case missingPrivateKey
case missingCertificate
var errorDescription : String ? {
switch self {
case . noTeam : return NSLocalizedString ( " Developer team could not be found. " , comment : " " )
case . noCertificate : return NSLocalizedString ( " Developer certificate could not be found. " , comment : " " )
case . missingPrivateKey : return NSLocalizedString ( " The certificate's private key could not be found. " , comment : " " )
case . missingCertificate : return NSLocalizedString ( " The certificate could not be found. " , comment : " " )
2019-06-05 18:05:21 -07:00
}
}
}
2019-06-10 15:03:47 -07:00
@objc ( AuthenticationOperation )
2020-03-06 17:08:35 -08:00
class AuthenticationOperation : ResultOperation < ( ALTTeam , ALTCertificate , ALTAppleAPISession ) >
2019-06-05 18:05:21 -07:00
{
2020-03-06 17:08:35 -08:00
let context : AuthenticatedOperationContext
2019-11-18 14:49:17 -08:00
2019-06-05 18:05:21 -07:00
private weak var presentingViewController : UIViewController ?
2019-09-07 15:29:19 -07:00
private lazy var navigationController : UINavigationController = {
let navigationController = self . storyboard . instantiateViewController ( withIdentifier : " navigationController " ) as ! UINavigationController
2019-10-28 13:16:55 -07:00
if #available ( iOS 13.0 , * )
{
navigationController . isModalInPresentation = true
}
2019-09-07 15:29:19 -07:00
return navigationController
} ( )
2019-06-05 18:05:21 -07:00
private lazy var storyboard = UIStoryboard ( name : " Authentication " , bundle : nil )
private var appleIDPassword : String ?
2019-09-07 15:29:19 -07:00
private var shouldShowInstructions = false
2019-06-05 18:05:21 -07:00
2020-01-08 12:41:02 -08:00
private let operationQueue = OperationQueue ( )
2019-10-03 13:09:38 -07:00
2019-11-18 14:49:17 -08:00
private var submitCodeAction : UIAlertAction ?
2020-03-06 17:08:35 -08:00
init ( context : AuthenticatedOperationContext , presentingViewController : UIViewController ? )
2019-06-05 18:05:21 -07:00
{
2020-03-06 17:08:35 -08:00
self . context = context
2019-06-05 18:05:21 -07:00
self . presentingViewController = presentingViewController
super . init ( )
2020-03-06 17:08:35 -08:00
self . context . authenticationOperation = self
2020-01-08 12:41:02 -08:00
self . operationQueue . name = " com.altstore.AuthenticationOperation "
2020-03-06 17:08:35 -08:00
self . progress . totalUnitCount = 4
2019-06-05 18:05:21 -07:00
}
override func main ( )
{
super . main ( )
2020-03-06 17:08:35 -08:00
if let error = self . context . error
2019-11-18 14:49:17 -08:00
{
self . finish ( . failure ( error ) )
return
}
2019-06-05 18:05:21 -07:00
// S i g n I n
2019-11-18 14:49:17 -08:00
self . signIn ( ) { ( result ) in
2019-06-10 15:03:47 -07:00
guard ! self . isCancelled else { return self . finish ( . failure ( OperationError . cancelled ) ) }
2019-06-05 18:05:21 -07:00
switch result
{
2019-06-10 15:03:47 -07:00
case . failure ( let error ) : self . finish ( . failure ( error ) )
2020-03-19 11:50:39 -07:00
case . success ( ( let account , let session ) ) :
2020-03-06 17:08:35 -08:00
self . context . session = session
2019-06-10 15:03:47 -07:00
self . progress . completedUnitCount += 1
2019-06-05 18:05:21 -07:00
// F e t c h T e a m
2019-11-18 14:49:17 -08:00
self . fetchTeam ( for : account , session : session ) { ( result ) in
2019-06-10 15:03:47 -07:00
guard ! self . isCancelled else { return self . finish ( . failure ( OperationError . cancelled ) ) }
2019-06-05 18:05:21 -07:00
switch result
{
2019-06-10 15:03:47 -07:00
case . failure ( let error ) : self . finish ( . failure ( error ) )
2019-06-05 18:05:21 -07:00
case . success ( let team ) :
2020-03-06 17:08:35 -08:00
self . context . team = team
2019-06-10 15:03:47 -07:00
self . progress . completedUnitCount += 1
2019-06-05 18:05:21 -07:00
// F e t c h C e r t i f i c a t e
2019-11-18 14:49:17 -08:00
self . fetchCertificate ( for : team , session : session ) { ( result ) in
2019-06-10 15:03:47 -07:00
guard ! self . isCancelled else { return self . finish ( . failure ( OperationError . cancelled ) ) }
2019-06-05 18:05:21 -07:00
switch result
{
2019-06-10 15:03:47 -07:00
case . failure ( let error ) : self . finish ( . failure ( error ) )
case . success ( let certificate ) :
2020-03-06 17:08:35 -08:00
self . context . certificate = certificate
2019-06-10 15:03:47 -07:00
self . progress . completedUnitCount += 1
2020-02-10 17:30:11 -08:00
2020-03-06 17:08:35 -08:00
// R e g i s t e r D e v i c e
self . registerCurrentDevice ( for : team , session : session ) { ( result ) in
2020-02-10 17:30:11 -08:00
guard ! self . isCancelled else { return self . finish ( . failure ( OperationError . cancelled ) ) }
switch result
{
case . failure ( let error ) : self . finish ( . failure ( error ) )
case . success :
2020-03-06 17:08:35 -08:00
self . progress . completedUnitCount += 1
2020-02-10 17:30:11 -08:00
2020-03-06 17:08:35 -08:00
// S a v e a c c o u n t / t e a m t o d i s k .
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 :
// M u s t c a c h e A p p I D s _ a f t e r _ s a v i n g a c c o u n t / t e a m t o d i s k .
self . cacheAppIDs ( team : team , session : session ) { ( result ) in
let result = result . map { _ in ( team , certificate , session ) }
self . finish ( result )
}
}
2020-02-10 17:30:11 -08:00
}
}
2019-11-18 14:49:17 -08:00
}
2019-06-05 18:05:21 -07:00
}
}
}
}
}
}
}
2019-06-10 15:03:47 -07:00
2020-02-10 17:30:11 -08:00
func save ( _ altTeam : ALTTeam , completionHandler : @ escaping ( Result < Void , Error > ) -> Void )
{
let context = DatabaseManager . shared . persistentContainer . newBackgroundContext ( )
context . performAndWait {
do
{
let account : Account
let team : Team
if let tempAccount = Account . first ( satisfying : NSPredicate ( format : " %K == %@ " , # keyPath ( Account . identifier ) , altTeam . account . identifier ) , in : context )
{
account = tempAccount
}
else
{
account = Account ( altTeam . account , context : context )
}
if let tempTeam = Team . first ( satisfying : NSPredicate ( format : " %K == %@ " , # keyPath ( Team . identifier ) , altTeam . identifier ) , in : context )
{
team = tempTeam
}
else
{
team = Team ( altTeam , account : account , context : context )
}
account . update ( account : altTeam . account )
team . update ( team : altTeam )
try context . save ( )
completionHandler ( . success ( ( ) ) )
}
catch
{
completionHandler ( . failure ( error ) )
}
}
}
2020-03-06 17:08:35 -08:00
override func finish ( _ result : Result < ( ALTTeam , ALTCertificate , ALTAppleAPISession ) , Error > )
2019-06-10 15:03:47 -07:00
{
guard ! self . isFinished else { return }
2020-03-06 17:08:35 -08:00
print ( " Finished authenticating with result: " , result . error ? . localizedDescription ? ? " success " )
2019-06-10 15:03:47 -07:00
let context = DatabaseManager . shared . persistentContainer . newBackgroundContext ( )
2020-02-10 17:30:11 -08:00
context . perform {
2019-06-10 15:03:47 -07:00
do
{
2020-03-06 17:08:35 -08:00
let ( altTeam , altCertificate , session ) = try result . get ( )
2020-02-10 17:30:11 -08:00
guard
2020-03-06 17:08:35 -08:00
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 )
2020-02-10 17:30:11 -08:00
else { throw AuthenticationError . noTeam }
2019-06-10 15:03:47 -07:00
// A c c o u n t
account . isActiveAccount = true
let otherAccountsFetchRequest = Account . fetchRequest ( ) as NSFetchRequest < Account >
otherAccountsFetchRequest . predicate = NSPredicate ( format : " %K != %@ " , # keyPath ( Account . identifier ) , account . identifier )
let otherAccounts = try context . fetch ( otherAccountsFetchRequest )
for account in otherAccounts
{
account . isActiveAccount = false
}
// T e a m
team . isActiveTeam = true
let otherTeamsFetchRequest = Team . fetchRequest ( ) as NSFetchRequest < Team >
otherTeamsFetchRequest . predicate = NSPredicate ( format : " %K != %@ " , # keyPath ( Team . identifier ) , team . identifier )
let otherTeams = try context . fetch ( otherTeamsFetchRequest )
for team in otherTeams
{
team . isActiveTeam = false
}
2020-03-11 14:43:19 -07:00
let activeAppsMinimumVersion = OperatingSystemVersion ( majorVersion : 13 , minorVersion : 3 , patchVersion : 1 )
if team . type = = . free , ProcessInfo . processInfo . isOperatingSystemAtLeast ( activeAppsMinimumVersion )
{
2020-04-01 13:05:31 -07:00
UserDefaults . standard . activeAppsLimit = ALTActiveAppsLimit
2020-03-11 14:43:19 -07:00
}
else
{
UserDefaults . standard . activeAppsLimit = nil
}
2019-06-10 15:03:47 -07:00
// S a v e
try context . save ( )
// U p d a t e k e y c h a i n
2020-03-06 17:08:35 -08:00
Keychain . shared . appleIDEmailAddress = altTeam . account . appleID
2019-06-10 15:03:47 -07:00
Keychain . shared . appleIDPassword = self . appleIDPassword
2020-03-06 17:08:35 -08:00
Keychain . shared . signingCertificate = altCertificate . p12Data ( )
Keychain . shared . signingCertificatePassword = altCertificate . machineIdentifier
2019-06-10 15:03:47 -07:00
2020-02-10 17:30:11 -08:00
self . showInstructionsIfNecessary ( ) { ( didShowInstructions ) in
2019-10-28 13:16:55 -07:00
2020-03-06 17:08:35 -08:00
let signer = ALTSigner ( team : altTeam , certificate : altCertificate )
2020-02-10 17:30:11 -08:00
// R e f r e s h s c r e e n m u s t g o l a s t s i n c e a s u c c e s s f u l r e f r e s h w i l l c a u s e t h e a p p t o q u i t .
self . showRefreshScreenIfNecessary ( signer : signer , session : session ) { ( didShowRefreshAlert ) in
super . finish ( result )
DispatchQueue . main . async {
self . navigationController . dismiss ( animated : true , completion : nil )
}
2019-10-28 13:16:55 -07:00
}
}
2019-06-10 15:03:47 -07:00
}
catch
{
2020-02-10 17:30:11 -08:00
super . finish ( result )
2019-10-28 13:16:55 -07:00
DispatchQueue . main . async {
self . navigationController . dismiss ( animated : true , completion : nil )
}
2019-06-10 15:03:47 -07:00
}
}
}
2019-06-05 18:05:21 -07:00
}
private extension AuthenticationOperation
{
func present ( _ viewController : UIViewController ) -> Bool
{
guard let presentingViewController = self . presentingViewController else { return false }
2019-09-07 15:29:19 -07:00
self . navigationController . view . tintColor = . white
2019-06-05 18:05:21 -07:00
if self . navigationController . viewControllers . isEmpty
{
2019-06-06 12:56:13 -07:00
guard presentingViewController . presentedViewController = = nil else { return false }
self . navigationController . setViewControllers ( [ viewController ] , animated : false )
2019-06-05 18:05:21 -07:00
presentingViewController . present ( self . navigationController , animated : true , completion : nil )
}
else
{
viewController . navigationItem . leftBarButtonItem = nil
self . navigationController . pushViewController ( viewController , animated : true )
}
return true
}
}
private extension AuthenticationOperation
{
2019-11-18 14:49:17 -08:00
func signIn ( completionHandler : @ escaping ( Result < ( ALTAccount , ALTAppleAPISession ) , Swift . Error > ) -> Void )
2019-06-05 18:05:21 -07:00
{
func authenticate ( )
{
DispatchQueue . main . async {
let authenticationViewController = self . storyboard . instantiateViewController ( withIdentifier : " authenticationViewController " ) as ! AuthenticationViewController
2019-11-18 14:49:17 -08:00
authenticationViewController . authenticationHandler = { ( appleID , password , completionHandler ) in
self . authenticate ( appleID : appleID , password : password ) { ( result ) in
completionHandler ( result )
}
}
authenticationViewController . completionHandler = { ( result ) in
if let ( account , session , password ) = result
2019-06-05 18:05:21 -07:00
{
2019-09-07 15:29:19 -07:00
// W e p r e s e n t e d t h e A u t h U I a n d t h e u s e r s i g n e d i n .
// I n t h i s c a s e , w e ' l l a s s u m e w e s h o u l d s h o w t h e i n s t r u c t i o n s a g a i n .
self . shouldShowInstructions = true
2019-06-05 18:05:21 -07:00
2019-09-07 15:29:19 -07:00
self . appleIDPassword = password
2019-11-18 14:49:17 -08:00
completionHandler ( . success ( ( account , session ) ) )
2019-06-05 18:05:21 -07:00
}
else
{
2019-06-10 15:03:47 -07:00
completionHandler ( . failure ( OperationError . cancelled ) )
2019-06-05 18:05:21 -07:00
}
}
if ! self . present ( authenticationViewController )
{
2019-06-10 15:03:47 -07:00
completionHandler ( . failure ( OperationError . notAuthenticated ) )
2019-06-05 18:05:21 -07:00
}
}
}
if let appleID = Keychain . shared . appleIDEmailAddress , let password = Keychain . shared . appleIDPassword
{
2019-11-18 14:49:17 -08:00
self . authenticate ( appleID : appleID , password : password ) { ( result ) in
switch result
2019-06-05 18:05:21 -07:00
{
2020-03-19 11:50:39 -07:00
case . success ( ( let account , let session ) ) :
2019-06-05 18:05:21 -07:00
self . appleIDPassword = password
2019-11-18 14:49:17 -08:00
completionHandler ( . success ( ( account , session ) ) )
2019-06-05 18:05:21 -07:00
2019-11-18 14:49:17 -08:00
case . failure ( ALTAppleAPIError . incorrectCredentials ) , . failure ( ALTAppleAPIError . appSpecificPasswordRequired ) :
2019-06-05 18:05:21 -07:00
authenticate ( )
2019-11-18 14:49:17 -08:00
case . failure ( let error ) :
2019-06-05 18:05:21 -07:00
completionHandler ( . failure ( error ) )
}
}
}
else
{
authenticate ( )
}
}
2019-11-18 14:49:17 -08:00
func authenticate ( appleID : String , password : String , completionHandler : @ escaping ( Result < ( ALTAccount , ALTAppleAPISession ) , Swift . Error > ) -> Void )
{
2020-03-06 17:08:35 -08:00
let fetchAnisetteDataOperation = FetchAnisetteDataOperation ( context : self . context )
2020-01-08 12:41:02 -08:00
fetchAnisetteDataOperation . resultHandler = { ( result ) in
2019-11-18 14:49:17 -08:00
switch result
{
case . failure ( let error ) : completionHandler ( . failure ( error ) )
2020-01-08 12:41:02 -08:00
case . success ( let anisetteData ) :
let verificationHandler : ( ( @ escaping ( String ? ) -> Void ) -> Void ) ?
2019-11-18 14:49:17 -08:00
2020-01-08 12:41:02 -08:00
if let presentingViewController = self . presentingViewController
{
verificationHandler = { ( completionHandler ) in
DispatchQueue . main . async {
let alertController = UIAlertController ( title : NSLocalizedString ( " Please enter the 6-digit verification code that was sent to your Apple devices. " , comment : " " ) , message : nil , preferredStyle : . alert )
alertController . addTextField { ( textField ) in
textField . autocorrectionType = . no
textField . autocapitalizationType = . none
textField . keyboardType = . numberPad
2019-11-18 14:49:17 -08:00
2020-01-08 12:41:02 -08:00
NotificationCenter . default . addObserver ( self , selector : #selector ( AuthenticationOperation . textFieldTextDidChange ( _ : ) ) , name : UITextField . textDidChangeNotification , object : textField )
}
let submitAction = UIAlertAction ( title : NSLocalizedString ( " Continue " , comment : " " ) , style : . default ) { ( action ) in
let textField = alertController . textFields ? . first
2019-11-18 14:49:17 -08:00
2020-01-08 12:41:02 -08:00
let code = textField ? . text ? ? " "
completionHandler ( code )
}
submitAction . isEnabled = false
alertController . addAction ( submitAction )
self . submitCodeAction = submitAction
alertController . addAction ( UIAlertAction ( title : RSTSystemLocalizedString ( " Cancel " ) , style : . cancel ) { ( action ) in
completionHandler ( nil )
} )
if self . navigationController . presentingViewController != nil
{
self . navigationController . present ( alertController , animated : true , completion : nil )
}
else
{
presentingViewController . present ( alertController , animated : true , completion : nil )
2019-11-18 14:49:17 -08:00
}
}
}
}
2020-01-08 12:41:02 -08:00
else
{
// N o v i e w c o n t r o l l e r t o p r e s e n t s e c u r i t y c o d e a l e r t , s o d o n ' t p r o v i d e v e r i f i c a t i o n H a n d l e r .
verificationHandler = nil
}
ALTAppleAPI . shared . authenticate ( appleID : appleID , password : password , anisetteData : anisetteData ,
verificationHandler : verificationHandler ) { ( account , session , error ) in
if let account = account , let session = session
{
completionHandler ( . success ( ( account , session ) ) )
}
else
{
completionHandler ( . failure ( error ? ? OperationError . unknown ) )
}
}
2019-11-18 14:49:17 -08:00
}
}
2020-01-08 12:41:02 -08:00
self . operationQueue . addOperation ( fetchAnisetteDataOperation )
2019-11-18 14:49:17 -08:00
}
func fetchTeam ( for account : ALTAccount , session : ALTAppleAPISession , completionHandler : @ escaping ( Result < ALTTeam , Swift . Error > ) -> Void )
2019-06-05 18:05:21 -07:00
{
func selectTeam ( from teams : [ ALTTeam ] )
{
2019-09-07 15:29:19 -07:00
if let team = teams . first ( where : { $0 . type = = . free } )
2019-07-31 14:33:23 -07:00
{
return completionHandler ( . success ( team ) )
}
2019-09-07 15:29:19 -07:00
else if let team = teams . first ( where : { $0 . type = = . individual } )
{
return completionHandler ( . success ( team ) )
}
else if let team = teams . first
{
return completionHandler ( . success ( team ) )
}
else
{
return completionHandler ( . failure ( AuthenticationError . noTeam ) )
2019-06-05 18:05:21 -07:00
}
}
2019-09-07 15:29:19 -07:00
2019-11-18 14:49:17 -08:00
ALTAppleAPI . shared . fetchTeams ( for : account , session : session ) { ( teams , error ) in
2019-06-05 18:05:21 -07:00
switch Result ( teams , error )
{
case . failure ( let error ) : completionHandler ( . failure ( error ) )
case . success ( let teams ) :
DatabaseManager . shared . persistentContainer . performBackgroundTask { ( context ) in
2019-06-06 14:46:23 -07:00
if let activeTeam = DatabaseManager . shared . activeTeam ( in : context ) , let altTeam = teams . first ( where : { $0 . identifier = = activeTeam . identifier } )
2019-06-05 18:05:21 -07:00
{
2019-06-06 14:46:23 -07:00
completionHandler ( . success ( altTeam ) )
2019-06-05 18:05:21 -07:00
}
2019-06-06 14:46:23 -07:00
else
2019-06-05 18:05:21 -07:00
{
selectTeam ( from : teams )
}
}
}
}
}
2019-11-18 14:49:17 -08:00
func fetchCertificate ( for team : ALTTeam , session : ALTAppleAPISession , completionHandler : @ escaping ( Result < ALTCertificate , Swift . Error > ) -> Void )
2019-06-05 18:05:21 -07:00
{
func requestCertificate ( )
{
let machineName = " AltStore - " + UIDevice . current . name
2019-11-18 14:49:17 -08:00
ALTAppleAPI . shared . addCertificate ( machineName : machineName , to : team , session : session ) { ( certificate , error ) in
2019-06-05 18:05:21 -07:00
do
{
let certificate = try Result ( certificate , error ) . get ( )
2019-06-10 15:03:47 -07:00
guard let privateKey = certificate . privateKey else { throw AuthenticationError . missingPrivateKey }
2019-06-05 18:05:21 -07:00
2019-11-18 14:49:17 -08:00
ALTAppleAPI . shared . fetchCertificates ( for : team , session : session ) { ( certificates , error ) in
2019-06-05 18:05:21 -07:00
do
{
let certificates = try Result ( certificates , error ) . get ( )
2019-06-18 17:40:30 -07:00
guard let certificate = certificates . first ( where : { $0 . serialNumber = = certificate . serialNumber } ) else {
2019-06-10 15:03:47 -07:00
throw AuthenticationError . missingCertificate
2019-06-05 18:05:21 -07:00
}
certificate . privateKey = privateKey
completionHandler ( . success ( certificate ) )
}
catch
{
completionHandler ( . failure ( error ) )
}
}
}
catch
{
completionHandler ( . failure ( error ) )
}
}
}
func replaceCertificate ( from certificates : [ ALTCertificate ] )
{
2019-09-07 15:29:19 -07:00
guard let certificate = certificates . first else { return completionHandler ( . failure ( AuthenticationError . noCertificate ) ) }
2019-07-31 14:33:23 -07:00
2019-11-18 14:49:17 -08:00
ALTAppleAPI . shared . revoke ( certificate , for : team , session : session ) { ( success , error ) in
2019-09-07 15:29:19 -07:00
if let error = error , ! success
{
completionHandler ( . failure ( error ) )
2019-06-05 18:05:21 -07:00
}
2019-09-07 15:29:19 -07:00
else
2019-06-05 18:05:21 -07:00
{
2019-09-07 15:29:19 -07:00
requestCertificate ( )
2019-06-05 18:05:21 -07:00
}
}
}
2019-11-18 14:49:17 -08:00
ALTAppleAPI . shared . fetchCertificates ( for : team , session : session ) { ( certificates , error ) in
2019-06-05 18:05:21 -07:00
do
{
let certificates = try Result ( certificates , error ) . get ( )
if
2019-10-28 13:16:55 -07:00
let data = Keychain . shared . signingCertificate ,
let localCertificate = ALTCertificate ( p12Data : data , password : nil ) ,
let certificate = certificates . first ( where : { $0 . serialNumber = = localCertificate . serialNumber } )
{
// W e h a v e a c e r t i f i c a t e s t o r e d i n t h e k e y c h a i n a n d i t h a s n ' t b e e n r e v o k e d .
localCertificate . machineIdentifier = certificate . machineIdentifier
completionHandler ( . success ( localCertificate ) )
}
else if
2019-06-18 17:40:30 -07:00
let serialNumber = Keychain . shared . signingCertificateSerialNumber ,
2019-06-05 18:05:21 -07:00
let privateKey = Keychain . shared . signingCertificatePrivateKey ,
2019-06-18 17:40:30 -07:00
let certificate = certificates . first ( where : { $0 . serialNumber = = serialNumber } )
2019-06-05 18:05:21 -07:00
{
2019-10-28 13:16:55 -07:00
// L E G A C Y
// W e h a v e t h e p r i v a t e k e y f o r o n e o f t h e c e r t i f i c a t e s , s o a d d i t t o c e r t i f i c a t e a n d u s e i t .
2019-06-05 18:05:21 -07:00
certificate . privateKey = privateKey
completionHandler ( . success ( certificate ) )
}
2019-10-28 13:16:55 -07:00
else if
let serialNumber = Bundle . main . object ( forInfoDictionaryKey : Bundle . Info . certificateID ) as ? String ,
let certificate = certificates . first ( where : { $0 . serialNumber = = serialNumber } ) ,
let machineIdentifier = certificate . machineIdentifier ,
FileManager . default . fileExists ( atPath : Bundle . main . certificateURL . path ) ,
let data = try ? Data ( contentsOf : Bundle . main . certificateURL ) ,
let localCertificate = ALTCertificate ( p12Data : data , password : machineIdentifier )
{
// W e h a v e a n e m b e d d e d c e r t i f i c a t e t h a t h a s n ' t b e e n r e v o k e d .
localCertificate . machineIdentifier = machineIdentifier
completionHandler ( . success ( localCertificate ) )
}
2019-06-05 18:05:21 -07:00
else if certificates . isEmpty
{
2019-10-28 13:16:55 -07:00
// N o c e r t i f i c a t e s , s o r e q u e s t a n e w o n e .
2019-06-05 18:05:21 -07:00
requestCertificate ( )
}
else
{
2019-10-28 13:16:55 -07:00
// W e d o n ' t h a v e p r i v a t e k e y s f o r a n y o f t h e c e r t i f i c a t e s ,
// s o w e n e e d t o r e v o k e o n e a n d c r e a t e a n e w o n e .
2019-06-05 18:05:21 -07:00
replaceCertificate ( from : certificates )
}
}
catch
{
completionHandler ( . failure ( error ) )
}
}
}
2020-03-06 17:08:35 -08:00
func registerCurrentDevice ( for team : ALTTeam , session : ALTAppleAPISession , completionHandler : @ escaping ( Result < ALTDevice , Error > ) -> Void )
2020-02-10 17:30:11 -08:00
{
2020-03-06 17:08:35 -08:00
guard let udid = Bundle . main . object ( forInfoDictionaryKey : Bundle . Info . deviceID ) as ? String else {
return completionHandler ( . failure ( OperationError . unknownUDID ) )
}
2020-02-10 17:30:11 -08:00
2020-12-03 14:45:34 -06:00
ALTAppleAPI . shared . fetchDevices ( for : team , types : [ . iphone , . ipad ] , session : session ) { ( devices , error ) in
2020-03-06 17:08:35 -08:00
do
{
let devices = try Result ( devices , error ) . get ( )
if let device = devices . first ( where : { $0 . identifier = = udid } )
{
completionHandler ( . success ( device ) )
}
else
{
2020-12-03 14:45:34 -06:00
ALTAppleAPI . shared . registerDevice ( name : UIDevice . current . name , identifier : udid , type : . iphone , team : team , session : session ) { ( device , error ) in
2020-03-06 17:08:35 -08:00
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 )
2020-02-10 17:30:11 -08:00
fetchAppIDsOperation . resultHandler = { ( result ) in
do
{
let ( _ , context ) = try result . get ( )
try context . save ( )
completionHandler ( . success ( ( ) ) )
}
catch
{
completionHandler ( . failure ( error ) )
}
}
self . operationQueue . addOperation ( fetchAppIDsOperation )
}
2019-09-07 15:29:19 -07:00
func showInstructionsIfNecessary ( completionHandler : @ escaping ( Bool ) -> Void )
{
guard self . shouldShowInstructions else { return completionHandler ( false ) }
DispatchQueue . main . async {
let instructionsViewController = self . storyboard . instantiateViewController ( withIdentifier : " instructionsViewController " ) as ! InstructionsViewController
instructionsViewController . showsBottomButton = true
instructionsViewController . completionHandler = {
completionHandler ( true )
}
if ! self . present ( instructionsViewController )
{
completionHandler ( false )
}
}
}
2019-10-28 13:16:55 -07:00
2020-02-10 17:30:11 -08:00
func showRefreshScreenIfNecessary ( signer : ALTSigner , session : ALTAppleAPISession , completionHandler : @ escaping ( Bool ) -> Void )
2019-10-03 13:09:38 -07:00
{
2019-10-28 13:16:55 -07:00
guard let application = ALTApplication ( fileURL : Bundle . main . bundleURL ) , let provisioningProfile = application . provisioningProfile else { return completionHandler ( false ) }
// I f w e ' r e n o t u s i n g t h e s a m e c e r t i f i c a t e u s e d t o i n s t a l l A l t S t o r e , w a r n u s e r t h a t t h e y n e e d t o r e f r e s h .
guard ! provisioningProfile . certificates . contains ( signer . certificate ) else { return completionHandler ( false ) }
2020-01-21 17:14:16 -08:00
#if DEBUG
completionHandler ( false )
#else
2019-10-28 13:16:55 -07:00
DispatchQueue . main . async {
2020-03-30 15:23:20 -07:00
let context = AuthenticatedOperationContext ( context : self . context )
context . operations . removeAllObjects ( ) // P r e v e n t d e a d l o c k d u e t o e n d l e s s w a i t i n g o n p r e v i o u s o p e r a t i o n s t o f i n i s h .
2019-10-28 13:16:55 -07:00
let refreshViewController = self . storyboard . instantiateViewController ( withIdentifier : " refreshAltStoreViewController " ) as ! RefreshAltStoreViewController
2020-03-30 15:23:20 -07:00
refreshViewController . context = context
2019-10-28 13:16:55 -07:00
refreshViewController . completionHandler = { _ in
completionHandler ( true )
}
if ! self . present ( refreshViewController )
{
completionHandler ( false )
}
2019-10-03 13:09:38 -07:00
}
2020-01-21 17:14:16 -08:00
#endif
2019-10-03 13:09:38 -07:00
}
}
2019-11-18 14:49:17 -08:00
extension AuthenticationOperation
{
@objc func textFieldTextDidChange ( _ notification : Notification )
{
guard let textField = notification . object as ? UITextField else { return }
self . submitCodeAction ? . isEnabled = ( textField . text ? ? " " ) . count = = 6
}
}