mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Asks user for permission before installing apps with private entitlements
This commit is contained in:
@@ -173,6 +173,7 @@
|
||||
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; };
|
||||
BFC57A702416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */; };
|
||||
BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC84A4C2421A19100853474 /* SourcesViewController.swift */; };
|
||||
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCB519245E3401001853EA /* VerifyAppOperation.swift */; };
|
||||
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476D2284B9A500981D42 /* AppDelegate.swift */; };
|
||||
BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; };
|
||||
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFD247762284B9A700981D42 /* Assets.xcassets */; };
|
||||
@@ -508,6 +509,7 @@
|
||||
BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledAppsCollectionHeaderView.swift; sourceTree = "<group>"; };
|
||||
BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InstalledAppsCollectionHeaderView.xib; sourceTree = "<group>"; };
|
||||
BFC84A4C2421A19100853474 /* SourcesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesViewController.swift; sourceTree = "<group>"; };
|
||||
BFCCB519245E3401001853EA /* VerifyAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifyAppOperation.swift; sourceTree = "<group>"; };
|
||||
BFD2476A2284B9A500981D42 /* AltStore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltStore.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFD2476D2284B9A500981D42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
BFD247742284B9A500981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
@@ -1242,6 +1244,7 @@
|
||||
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */,
|
||||
BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */,
|
||||
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */,
|
||||
BFCCB519245E3401001853EA /* VerifyAppOperation.swift */,
|
||||
);
|
||||
path = Operations;
|
||||
sourceTree = "<group>";
|
||||
@@ -1756,6 +1759,7 @@
|
||||
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
||||
BFD5D6F4230DDB0A007955AB /* Campaign.swift in Sources */,
|
||||
BFB6B21B23186D640022A802 /* NewsItem.swift in Sources */,
|
||||
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */,
|
||||
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */,
|
||||
BFA8172D23C5823E001B5953 /* InstalledExtension.swift in Sources */,
|
||||
BFD5D6E8230CC961007955AB /* PatreonAPI.swift in Sources */,
|
||||
|
||||
@@ -25,3 +25,18 @@ extension NSError
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
protocol ALTLocalizedError: LocalizedError, CustomNSError
|
||||
{
|
||||
var errorFailure: String? { get }
|
||||
}
|
||||
|
||||
extension ALTLocalizedError
|
||||
{
|
||||
var errorUserInfo: [String : Any] {
|
||||
let userInfo = [NSLocalizedDescriptionKey: self.errorDescription,
|
||||
NSLocalizedFailureReasonErrorKey: self.failureReason,
|
||||
NSLocalizedFailureErrorKey: self.errorFailure].compactMapValues { $0 }
|
||||
return userInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,6 +396,11 @@ private extension AppManager
|
||||
self.set(progress, for: operation)
|
||||
}
|
||||
|
||||
if let viewController = presentingViewController
|
||||
{
|
||||
group.context.presentingViewController = viewController
|
||||
}
|
||||
|
||||
/* Authenticate (if necessary) */
|
||||
var authenticationOperation: AuthenticationOperation?
|
||||
if group.context.session == nil
|
||||
@@ -503,6 +508,16 @@ private extension AppManager
|
||||
}
|
||||
progress.addChild(downloadOperation.progress, withPendingUnitCount: 25)
|
||||
|
||||
/* Verify App */
|
||||
let verifyOperation = VerifyAppOperation(context: context)
|
||||
verifyOperation.resultHandler = { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): context.error = error
|
||||
case .success: break
|
||||
}
|
||||
}
|
||||
verifyOperation.addDependency(downloadOperation)
|
||||
|
||||
/* Refresh Anisette Data */
|
||||
let refreshAnisetteDataOperation = FetchAnisetteDataOperation(context: group.context)
|
||||
@@ -513,7 +528,7 @@ private extension AppManager
|
||||
case .success(let anisetteData): group.context.session?.anisetteData = anisetteData
|
||||
}
|
||||
}
|
||||
refreshAnisetteDataOperation.addDependency(downloadOperation)
|
||||
refreshAnisetteDataOperation.addDependency(verifyOperation)
|
||||
|
||||
|
||||
/* Fetch Provisioning Profiles */
|
||||
@@ -579,7 +594,7 @@ private extension AppManager
|
||||
progress.addChild(installOperation.progress, withPendingUnitCount: 30)
|
||||
installOperation.addDependency(sendAppOperation)
|
||||
|
||||
let operations = [downloadOperation, refreshAnisetteDataOperation, fetchProvisioningProfilesOperation, resignAppOperation, sendAppOperation, installOperation]
|
||||
let operations = [downloadOperation, verifyOperation, refreshAnisetteDataOperation, fetchProvisioningProfilesOperation, resignAppOperation, sendAppOperation, installOperation]
|
||||
group.add(operations)
|
||||
self.run(operations, context: group.context)
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class OperationContext
|
||||
var server: Server?
|
||||
var error: Error?
|
||||
|
||||
var presentingViewController: UIViewController?
|
||||
|
||||
let operations: NSHashTable<Foundation.Operation>
|
||||
|
||||
init(server: Server? = nil, error: Error? = nil, operations: [Foundation.Operation] = [])
|
||||
|
||||
131
AltStore/Operations/VerifyAppOperation.swift
Normal file
131
AltStore/Operations/VerifyAppOperation.swift
Normal file
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// VerifyAppOperation.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 5/2/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import AltSign
|
||||
import AltKit
|
||||
|
||||
import Roxas
|
||||
|
||||
enum VerificationError: ALTLocalizedError
|
||||
{
|
||||
case privateEntitlements(ALTApplication, [String: Any])
|
||||
|
||||
var app: ALTApplication {
|
||||
switch self {
|
||||
case .privateEntitlements(let app, _): return app
|
||||
}
|
||||
}
|
||||
|
||||
var errorFailure: String? {
|
||||
return String(format: NSLocalizedString("“%@” could not be installed.", comment: ""), app.name)
|
||||
}
|
||||
|
||||
var failureReason: String? {
|
||||
switch self
|
||||
{
|
||||
case .privateEntitlements(let app, _):
|
||||
return String(format: NSLocalizedString("“%@” requires private permissions.", comment: ""), app.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(VerifyAppOperation)
|
||||
class VerifyAppOperation: ResultOperation<Void>
|
||||
{
|
||||
let context: AppOperationContext
|
||||
var verificationHandler: ((VerificationError) -> Bool)?
|
||||
|
||||
init(context: AppOperationContext)
|
||||
{
|
||||
self.context = context
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func main()
|
||||
{
|
||||
super.main()
|
||||
|
||||
do
|
||||
{
|
||||
if let error = self.context.error
|
||||
{
|
||||
throw error
|
||||
}
|
||||
|
||||
guard let app = self.context.app else { throw OperationError.invalidParameters }
|
||||
|
||||
if let commentStart = app.entitlementsString.range(of: "<!---><!-->"), let commentEnd = app.entitlementsString.range(of: "<!-- -->")
|
||||
{
|
||||
// Psychic Paper private entitlements.
|
||||
|
||||
let entitlementsStart = app.entitlementsString.index(after: commentStart.upperBound)
|
||||
let rawEntitlements = String(app.entitlementsString[entitlementsStart ..< commentEnd.lowerBound])
|
||||
|
||||
let plistTemplate = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
%@
|
||||
</dict>
|
||||
</plist>
|
||||
"""
|
||||
let entitlementsPlist = String(format: plistTemplate, rawEntitlements)
|
||||
let entitlements = try PropertyListSerialization.propertyList(from: entitlementsPlist.data(using: .utf8)!, options: [], format: nil) as! [String: Any]
|
||||
|
||||
let error = VerificationError.privateEntitlements(app, entitlements)
|
||||
self.process(error) { (result) in
|
||||
self.finish(result.mapError { $0 as Error })
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
self.finish(.success(()))
|
||||
}
|
||||
catch
|
||||
{
|
||||
self.finish(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension VerifyAppOperation
|
||||
{
|
||||
func process(_ error: VerificationError, completion: @escaping (Result<Void, VerificationError>) -> Void)
|
||||
{
|
||||
guard let presentingViewController = self.context.presentingViewController else { return completion(.failure(error)) }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
switch error
|
||||
{
|
||||
case .privateEntitlements(_, let entitlements):
|
||||
let permissions = entitlements.keys.sorted().joined(separator: "\n")
|
||||
let message = String(format: NSLocalizedString("""
|
||||
You must allow access to these private permissions before continuing:
|
||||
|
||||
%@
|
||||
|
||||
Private permissions allow apps to do more than normally allowed by iOS, including potentially accessing sensitive private data. Make sure to only install apps from sources you trust.
|
||||
""", comment: ""), permissions)
|
||||
|
||||
let alertController = UIAlertController(title: error.failureReason ?? error.localizedDescription, message: message, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Allow Access", comment: ""), style: .destructive) { (action) in
|
||||
completion(.success(()))
|
||||
})
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Deny Access", comment: ""), style: .default, handler: { (action) in
|
||||
completion(.failure(error))
|
||||
}))
|
||||
presentingViewController.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user