Revises appPermissions JSON format

• Split into `entitlements` and `privacy` sections
• `entitlements` is an array of entitlement keys
• `privacy` is a dictionary mapping UsageDescription keys to their descriptions
This commit is contained in:
Riley Testut
2023-10-10 14:47:00 -05:00
committed by Magesh K
parent cd42cc827f
commit 34c503da4b
5 changed files with 197 additions and 73 deletions

View File

@@ -294,41 +294,23 @@ private extension VerifyAppOperation
// Privacy
let allPrivacyPermissions: Set<ALTAppPrivacyPermission>
if #available(iOS 16, *)
{
let regex = Regex {
"NS"
// Capture permission "name"
Capture {
OneOrMore(.anyGraphemeCluster)
let allPrivacyPermissions = ([app] + app.appExtensions).flatMap { (app) in
let permissions = app.bundle.infoDictionary?.keys.compactMap { key -> ALTAppPrivacyPermission? in
if #available(iOS 16, *)
{
guard key.wholeMatch(of: Regex.privacyPermission) != nil else { return nil }
}
else
{
guard key.contains("UsageDescription") else { return nil }
}
"UsageDescription"
// Optional suffix
Optionally(OneOrMore(.anyGraphemeCluster))
}
let permission = ALTAppPrivacyPermission(rawValue: key)
return permission
} ?? []
let privacyPermissions = ([app] + app.appExtensions).flatMap { (app) in
let permissions = app.bundle.infoDictionary?.keys.compactMap { key -> ALTAppPrivacyPermission? in
guard let match = key.wholeMatch(of: regex) else { return nil }
let permission = ALTAppPrivacyPermission(rawValue: String(match.1))
return permission
} ?? []
return permissions
}
allPrivacyPermissions = Set(privacyPermissions)
return permissions
}
else
{
allPrivacyPermissions = []
}
// Verify permissions.
let sourcePermissions: Set<AnyHashable> = Set(await $storeApp.perform { $0.permissions.map { AnyHashable($0.permission) } })
@@ -336,8 +318,37 @@ private extension VerifyAppOperation
// To pass: EVERY permission in localPermissions must also appear in sourcePermissions.
// If there is a single missing permission, throw error.
let missingPermissions: [any ALTAppPermission] = localPermissions.filter { !sourcePermissions.contains(AnyHashable($0)) }
guard missingPermissions.isEmpty else { throw VerificationError.undeclaredPermissions(missingPermissions, app: app) }
let missingPermissions: [any ALTAppPermission] = localPermissions.filter { permission in
if sourcePermissions.contains(AnyHashable(permission))
{
// `permission` exists in source, so return false.
return false
}
else if permission.type == .privacy
{
guard #available(iOS 16, *) else {
// Assume all privacy permissions _are_ included in source on pre-iOS 16 devices.
return false
}
// Special-handling for legacy privacy permissions.
if let match = permission.rawValue.firstMatch(of: Regex.privacyPermission),
case let legacyPermission = ALTAppPrivacyPermission(rawValue: String(match.1)),
sourcePermissions.contains(AnyHashable(legacyPermission))
{
// The legacy name of this permission exists in the source, so return false.
return false
}
}
// Source doesn't contain permission or its legacy name, so assume it is missing.
return true
}
guard missingPermissions.isEmpty else {
// There is at least one undeclared permission, so throw error.
throw VerificationError.undeclaredPermissions(missingPermissions, app: app)
}
return localPermissions
}