Verifies downloaded app’s permissions match source

Renames source JSON permissions key to “appPermissions” in order to preserve backwards compatibility, since we’ve changed the schema for permissions.
This commit is contained in:
Riley Testut
2023-05-12 18:26:24 -05:00
parent 729eb89433
commit 45ed24bfb5
16 changed files with 449 additions and 130 deletions

View File

@@ -9,48 +9,31 @@
import CoreData
import UIKit
public extension ALTAppPermissionType
{
var localizedShortName: String? {
switch self
{
case .photos: return NSLocalizedString("Photos", comment: "")
case .backgroundAudio: return NSLocalizedString("Audio (BG)", comment: "")
case .backgroundFetch: return NSLocalizedString("Fetch (BG)", comment: "")
default: return nil
}
}
var localizedName: String? {
switch self
{
case .photos: return NSLocalizedString("Photos", comment: "")
case .backgroundAudio: return NSLocalizedString("Background Audio", comment: "")
case .backgroundFetch: return NSLocalizedString("Background Fetch", comment: "")
default: return nil
}
}
var icon: UIImage? {
switch self
{
case .photos: return UIImage(named: "PhotosPermission")
case .backgroundAudio: return UIImage(named: "BackgroundAudioPermission")
case .backgroundFetch: return UIImage(named: "BackgroundFetchPermission")
default: return nil
}
}
}
import AltSign
@objc(AppPermission)
@objc(AppPermission) @dynamicMemberLookup
public class AppPermission: NSManagedObject, Decodable, Fetchable
{
/* Properties */
@NSManaged public var type: ALTAppPermissionType
@NSManaged public var usageDescription: String
@NSManaged public var usageDescription: String?
@nonobjc public var permission: any ALTAppPermission {
switch self.type
{
case .entitlement: return ALTEntitlement(rawValue: self._permission)
case .privacy: return ALTAppPrivacyPermission(rawValue: self._permission)
case .backgroundMode: return ALTAppBackgroundMode(rawValue: self._permission)
default: return UnknownAppPermission(rawValue: self._permission)
}
}
@NSManaged @objc(permission) private var _permission: String
// Set by StoreApp.
@NSManaged public var appBundleID: String?
/* Relationships */
@NSManaged public private(set) var app: StoreApp!
@NSManaged public internal(set) var app: StoreApp?
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
{
@@ -59,7 +42,10 @@ public class AppPermission: NSManagedObject, Decodable, Fetchable
private enum CodingKeys: String, CodingKey
{
case type
case entitlement
case privacyType = "privacy"
case backgroundMode = "background"
case usageDescription
}
@@ -72,10 +58,33 @@ public class AppPermission: NSManagedObject, Decodable, Fetchable
do
{
let container = try decoder.container(keyedBy: CodingKeys.self)
self.usageDescription = try container.decode(String.self, forKey: .usageDescription)
let rawType = try container.decode(String.self, forKey: .type)
self.type = ALTAppPermissionType(rawValue: rawType)
self.usageDescription = try container.decodeIfPresent(String.self, forKey: .usageDescription)
if let entitlement = try container.decodeIfPresent(String.self, forKey: .entitlement)
{
self._permission = entitlement
self.type = .entitlement
}
else if let privacyType = try container.decodeIfPresent(String.self, forKey: .privacyType)
{
self._permission = privacyType
self.type = .privacy
}
else if let backgroundMode = try container.decodeIfPresent(String.self, forKey: .backgroundMode)
{
self._permission = backgroundMode
self.type = .backgroundMode
}
else
{
self._permission = ""
self.type = .unknown
// We don't want to save any unknown permissions, but can't throw error
// without making the entire decoding fail, so just delete self instead.
context.delete(self)
}
}
catch
{
@@ -96,3 +105,14 @@ public extension AppPermission
return NSFetchRequest<AppPermission>(entityName: "AppPermission")
}
}
// @dynamicMemberLookup
public extension AppPermission
{
// Convenience for accessing .permission properties.
subscript<T>(dynamicMember keyPath: KeyPath<any ALTAppPermission, T>) -> T {
get {
return self.permission[keyPath: keyPath]
}
}
}