diff --git a/README.md b/README.md index 71258b0..e9282d1 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,6 @@ To sign into the Mac App Store for the first time run `mas signin`. ```bash $ mas signin mas@example.com -==> Signing in to Apple ID: mas@example.com Password: ``` @@ -171,15 +170,13 @@ If you experience issues signing in this way, you can ask to sign in using a gra (provided by Mac App Store application): ```bash -$ mas signin --dialog mas@example.com -==> Signing in to Apple ID: mas@example.com +mas signin --dialog mas@example.com ``` You can also embed your password in the command. ```bash -$ mas signin mas@example.com 'ZdkM4f$gzF;gX3ABXNLf8KcCt.x.np' -==> Signing in to Apple ID: mas@example.com +mas signin mas@example.com 'ZdkM4f$gzF;gX3ABXNLf8KcCt.x.np' ``` Use `mas signout` to sign out from the Mac App Store. diff --git a/Sources/MasKit/AppStore/ISStoreAccount.swift b/Sources/MasKit/AppStore/ISStoreAccount.swift index 5865332..c61866d 100644 --- a/Sources/MasKit/AppStore/ISStoreAccount.swift +++ b/Sources/MasKit/AppStore/ISStoreAccount.swift @@ -17,65 +17,73 @@ extension ISStoreAccount: StoreAccount { let group = DispatchGroup() group.enter() - let accountService: ISAccountService = ISServiceProxy.genericShared().accountService - accountService.primaryAccount { (storeAccount: ISStoreAccount) in + ISServiceProxy.genericShared().accountService.primaryAccount { storeAccount in account = storeAccount group.leave() } _ = group.wait(timeout: .now() + 30) } else { - // macOS 10.9-10.12 - let accountStore = CKAccountStore.shared() - account = accountStore.primaryAccount + account = CKAccountStore.shared().primaryAccount } return account } static func signIn(username: String, password: String, systemDialog: Bool = false) throws -> StoreAccount { - var storeAccount: ISStoreAccount? - var maserror: MASError? - - let accountService: ISAccountService = ISServiceProxy.genericShared().accountService - let client = ISStoreClient(storeClientType: 0) - accountService.setStoreClient(client) - - let context = ISAuthenticationContext(accountID: 0) - context.appleIDOverride = username - - if systemDialog { - context.appleIDOverride = username + if #available(macOS 10.13, *) { + // Signing in is no longer possible as of High Sierra. + // https://github.com/mas-cli/mas/issues/164 + throw MASError.notSupported } else { - context.demoMode = true - context.demoAccountName = username - context.demoAccountPassword = password - context.demoAutologinMode = true - } - - let group = DispatchGroup() - group.enter() - - // Only works on macOS Sierra and below - accountService.signIn(with: context) { success, account, error in - if success { - storeAccount = account - } else { - maserror = .signInFailed(error: error as NSError?) + let primaryAccount = primaryAccount + if primaryAccount != nil { + throw MASError.alreadySignedIn(asAccountId: primaryAccount!.identifier) } - group.leave() - } - if systemDialog { - group.wait() - } else { - _ = group.wait(timeout: .now() + 30) - } + let password = + password.isEmpty && !systemDialog + ? String(validatingUTF8: getpass("Password: "))! + : password - if let account = storeAccount { - return account - } + let accountService = ISServiceProxy.genericShared().accountService + accountService.setStoreClient(ISStoreClient(storeClientType: 0)) - throw maserror ?? MASError.signInFailed(error: nil) + let context = ISAuthenticationContext(accountID: 0) + context.appleIDOverride = username + if !systemDialog { + context.demoMode = true + context.demoAccountName = username + context.demoAccountPassword = password + context.demoAutologinMode = true + } + + let group = DispatchGroup() + group.enter() + + var storeAccount: ISStoreAccount? + var maserror: MASError? + // Only works on macOS Sierra and below + accountService.signIn(with: context) { success, account, error in + if success, let account { + storeAccount = account + } else { + maserror = .signInFailed(error: error as NSError?) + } + group.leave() + } + + if systemDialog { + group.wait() + } else { + _ = group.wait(timeout: .now() + 30) + } + + if let storeAccount { + return storeAccount + } + + throw maserror ?? MASError.signInFailed(error: nil) + } } } diff --git a/Sources/MasKit/Commands/Account.swift b/Sources/MasKit/Commands/Account.swift index 45249f7..9701054 100644 --- a/Sources/MasKit/Commands/Account.swift +++ b/Sources/MasKit/Commands/Account.swift @@ -25,9 +25,8 @@ public struct AccountCommand: CommandProtocol { } if let account = ISStoreAccount.primaryAccount { - print(String(describing: account.identifier)) + print(account.identifier) } else { - printError("Not signed in") return .failure(.notSignedIn) } return .success(()) diff --git a/Sources/MasKit/Commands/SignIn.swift b/Sources/MasKit/Commands/SignIn.swift index f1b7cf0..1ac241f 100644 --- a/Sources/MasKit/Commands/SignIn.swift +++ b/Sources/MasKit/Commands/SignIn.swift @@ -19,32 +19,16 @@ public struct SignInCommand: CommandProtocol { /// Runs the command. public func run(_ options: Options) -> Result { - if #available(macOS 10.13, *) { - // Signing in is no longer possible as of High Sierra. - // https://github.com/mas-cli/mas/issues/164 - return .failure(.notSupported) - } - - guard ISStoreAccount.primaryAccount == nil else { - return .failure(.alreadySignedIn) - } - do { - printInfo("Signing in to Apple ID: \(options.username)") - - let password: String = { - if options.password.isEmpty, !options.dialog { - return String(validatingUTF8: getpass("Password: "))! - } - return options.password - }() - - _ = try ISStoreAccount.signIn(username: options.username, password: password, systemDialog: options.dialog) - } catch let error as NSError { - return .failure(.signInFailed(error: error)) + _ = try ISStoreAccount.signIn( + username: options.username, + password: options.password, + systemDialog: options.dialog + ) + return .success(()) + } catch { + return .failure(error as? MASError ?? .signInFailed(error: error as NSError)) } - - return .success(()) } } diff --git a/Sources/MasKit/Commands/SignOut.swift b/Sources/MasKit/Commands/SignOut.swift index efdf467..b222b8c 100644 --- a/Sources/MasKit/Commands/SignOut.swift +++ b/Sources/MasKit/Commands/SignOut.swift @@ -19,8 +19,7 @@ public struct SignOutCommand: CommandProtocol { /// Runs the command. public func run(_: Options) -> Result { if #available(macOS 10.13, *) { - let accountService: ISAccountService = ISServiceProxy.genericShared().accountService - accountService.signOut() + ISServiceProxy.genericShared().accountService.signOut() } else { // Using CKAccountStore to sign out does nothing on High Sierra // https://github.com/mas-cli/mas/issues/129 diff --git a/Sources/MasKit/Errors/MASError.swift b/Sources/MasKit/Errors/MASError.swift index 716f4ea..aaa768f 100644 --- a/Sources/MasKit/Errors/MASError.swift +++ b/Sources/MasKit/Errors/MASError.swift @@ -13,7 +13,7 @@ public enum MASError: Error, Equatable { case notSignedIn case signInFailed(error: NSError?) - case alreadySignedIn + case alreadySignedIn(asAccountId: String) case purchaseFailed(error: NSError?) case downloadFailed(error: NSError?) @@ -52,8 +52,8 @@ extension MASError: CustomStringConvertible { return "Sign in failed" } - case .alreadySignedIn: - return "Already signed in" + case .alreadySignedIn(let accountId): + return "Already signed in as \(accountId)" case .purchaseFailed(let error): if let error { diff --git a/Tests/MasKitTests/Errors/MASErrorTestCase.swift b/Tests/MasKitTests/Errors/MASErrorTestCase.swift index cdefffd..38e4ae1 100644 --- a/Tests/MasKitTests/Errors/MASErrorTestCase.swift +++ b/Tests/MasKitTests/Errors/MASErrorTestCase.swift @@ -57,8 +57,8 @@ class MASErrorTestCase: XCTestCase { } func testAlreadySignedIn() { - error = .alreadySignedIn - XCTAssertEqual(error.description, "Already signed in") + error = .alreadySignedIn(asAccountId: "person@example.com") + XCTAssertEqual(error.description, "Already signed in as person@example.com") } func testPurchaseFailed() {