mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-19 03:33:36 +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 */; };
|
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; };
|
||||||
BFC57A702416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */; };
|
BFC57A702416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */; };
|
||||||
BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC84A4C2421A19100853474 /* SourcesViewController.swift */; };
|
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 */; };
|
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476D2284B9A500981D42 /* AppDelegate.swift */; };
|
||||||
BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; };
|
BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; };
|
||||||
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFD247762284B9A700981D42 /* Assets.xcassets */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
BFD247742284B9A500981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
@@ -1242,6 +1244,7 @@
|
|||||||
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */,
|
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */,
|
||||||
BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */,
|
BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */,
|
||||||
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */,
|
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */,
|
||||||
|
BFCCB519245E3401001853EA /* VerifyAppOperation.swift */,
|
||||||
);
|
);
|
||||||
path = Operations;
|
path = Operations;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1756,6 +1759,7 @@
|
|||||||
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
||||||
BFD5D6F4230DDB0A007955AB /* Campaign.swift in Sources */,
|
BFD5D6F4230DDB0A007955AB /* Campaign.swift in Sources */,
|
||||||
BFB6B21B23186D640022A802 /* NewsItem.swift in Sources */,
|
BFB6B21B23186D640022A802 /* NewsItem.swift in Sources */,
|
||||||
|
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */,
|
||||||
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */,
|
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */,
|
||||||
BFA8172D23C5823E001B5953 /* InstalledExtension.swift in Sources */,
|
BFA8172D23C5823E001B5953 /* InstalledExtension.swift in Sources */,
|
||||||
BFD5D6E8230CC961007955AB /* PatreonAPI.swift in Sources */,
|
BFD5D6E8230CC961007955AB /* PatreonAPI.swift in Sources */,
|
||||||
|
|||||||
@@ -25,3 +25,18 @@ extension NSError
|
|||||||
return error
|
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)
|
self.set(progress, for: operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let viewController = presentingViewController
|
||||||
|
{
|
||||||
|
group.context.presentingViewController = viewController
|
||||||
|
}
|
||||||
|
|
||||||
/* Authenticate (if necessary) */
|
/* Authenticate (if necessary) */
|
||||||
var authenticationOperation: AuthenticationOperation?
|
var authenticationOperation: AuthenticationOperation?
|
||||||
if group.context.session == nil
|
if group.context.session == nil
|
||||||
@@ -503,6 +508,16 @@ private extension AppManager
|
|||||||
}
|
}
|
||||||
progress.addChild(downloadOperation.progress, withPendingUnitCount: 25)
|
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 */
|
/* Refresh Anisette Data */
|
||||||
let refreshAnisetteDataOperation = FetchAnisetteDataOperation(context: group.context)
|
let refreshAnisetteDataOperation = FetchAnisetteDataOperation(context: group.context)
|
||||||
@@ -513,7 +528,7 @@ private extension AppManager
|
|||||||
case .success(let anisetteData): group.context.session?.anisetteData = anisetteData
|
case .success(let anisetteData): group.context.session?.anisetteData = anisetteData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshAnisetteDataOperation.addDependency(downloadOperation)
|
refreshAnisetteDataOperation.addDependency(verifyOperation)
|
||||||
|
|
||||||
|
|
||||||
/* Fetch Provisioning Profiles */
|
/* Fetch Provisioning Profiles */
|
||||||
@@ -579,7 +594,7 @@ private extension AppManager
|
|||||||
progress.addChild(installOperation.progress, withPendingUnitCount: 30)
|
progress.addChild(installOperation.progress, withPendingUnitCount: 30)
|
||||||
installOperation.addDependency(sendAppOperation)
|
installOperation.addDependency(sendAppOperation)
|
||||||
|
|
||||||
let operations = [downloadOperation, refreshAnisetteDataOperation, fetchProvisioningProfilesOperation, resignAppOperation, sendAppOperation, installOperation]
|
let operations = [downloadOperation, verifyOperation, refreshAnisetteDataOperation, fetchProvisioningProfilesOperation, resignAppOperation, sendAppOperation, installOperation]
|
||||||
group.add(operations)
|
group.add(operations)
|
||||||
self.run(operations, context: group.context)
|
self.run(operations, context: group.context)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class OperationContext
|
|||||||
var server: Server?
|
var server: Server?
|
||||||
var error: Error?
|
var error: Error?
|
||||||
|
|
||||||
|
var presentingViewController: UIViewController?
|
||||||
|
|
||||||
let operations: NSHashTable<Foundation.Operation>
|
let operations: NSHashTable<Foundation.Operation>
|
||||||
|
|
||||||
init(server: Server? = nil, error: Error? = nil, operations: [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