Improve Account, SignIn, SignOut & ISStoreAccount extension & associated code.

Prevent `ISAccountService.signIn(with:)` deprecation warning.

Improve macOS version handling encapsulation.

Output error if `StoreAccount.signin(…)` provides a `nil` `ISStoreAccount`.

Improve `signin` `"Already Signed In" error output.

Remove unnecessary output.

Simplify code.

Partial #562

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-10 17:47:59 -04:00
parent 622a154fb9
commit 8e671f2624
No known key found for this signature in database
7 changed files with 68 additions and 81 deletions

View file

@ -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.

View file

@ -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)
}
}
}

View file

@ -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(())

View file

@ -19,32 +19,16 @@ public struct SignInCommand: CommandProtocol {
/// Runs the command.
public func run(_ options: Options) -> Result<Void, MASError> {
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(())
}
}

View file

@ -19,8 +19,7 @@ public struct SignOutCommand: CommandProtocol {
/// Runs the command.
public func run(_: Options) -> Result<Void, MASError> {
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

View file

@ -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 {

View file

@ -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() {