From d0475c2ec3dbacad4361f5b249d0f55f1b439871 Mon Sep 17 00:00:00 2001 From: jdpy19 Date: Mon, 12 Oct 2026 16:51:34 -0400 Subject: [PATCH 1/3] fix: provision associated domains entitlement --- .../Model/Entitlements/EntitlementTypes.swift | 2 +- .../AssociatedDomainsEntitlementTests.swift | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Tests/XToolTests/AssociatedDomainsEntitlementTests.swift diff --git a/Sources/XKit/Model/Entitlements/EntitlementTypes.swift b/Sources/XKit/Model/Entitlements/EntitlementTypes.swift index 1d65a047..26224859 100644 --- a/Sources/XKit/Model/Entitlements/EntitlementTypes.swift +++ b/Sources/XKit/Model/Entitlements/EntitlementTypes.swift @@ -17,7 +17,7 @@ extension EntitlementContainer { TeamIdentifierEntitlement.self, KeychainAccessGroupsEntitlement.self, GetTaskAllowEntitlement.self, -// AssociatedDomainsEntitlement.self, + AssociatedDomainsEntitlement.self, APSEnvironmentEntitlement.self, AppGroupEntitlement.self, // NetworkExtensionEntitlement.self, diff --git a/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift b/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift new file mode 100644 index 00000000..af6a8cfa --- /dev/null +++ b/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift @@ -0,0 +1,37 @@ +import Foundation +import Testing +@testable import XKit + +@Test func associatedDomainsEntitlementMapsToDeveloperCapability() throws { + let plist = """ + + + + + com.apple.developer.associated-domains + + applinks:example.com + + + + """ + + let entitlements = try PropertyListDecoder().decode( + Entitlements.self, + from: Data(plist.utf8) + ) + let associatedDomains = try #require( + entitlements.entitlements().compactMap { $0 as? AssociatedDomainsEntitlement }.first + ) + + #expect(associatedDomains.rawValue == ["applinks:example.com"]) + #expect(associatedDomains.capability.capabilityType.value1 == .associatedDomains) + #expect(!associatedDomains.capability.isFree) + + let encoded = try PropertyListEncoder().encode(entitlements) + let dictionary = try #require( + PropertyListSerialization.propertyList(from: encoded, format: nil) as? [String: Any] + ) + + #expect(dictionary[AssociatedDomainsEntitlement.identifier] as? [String] == ["applinks:example.com"]) +} From bc1ae2948e29c99b8e91548f01601756696dd402 Mon Sep 17 00:00:00 2001 From: jdpy19 Date: Thu, 15 Oct 2026 10:05:36 -0400 Subject: [PATCH 2/3] test: cover associated domains capability filtering --- .../DeveloperServicesAddAppOperation.swift | 21 +++++++++++---- .../Model/Entitlements/EntitlementTypes.swift | 2 ++ .../AssociatedDomainsEntitlementTests.swift | 26 +++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift b/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift index d726e91e..31da19c6 100644 --- a/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift +++ b/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift @@ -37,6 +37,21 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation { self.root = root } + static func capabilities( + for entitlements: Entitlements, + isFreeTeam: Bool + ) throws -> [Components.Schemas.CapabilityType: [Components.Schemas.CapabilitySetting]] { + let parsedEntitlements = try entitlements.entitlements() + let eligibleEntitlements = isFreeTeam + ? parsedEntitlements.filter { $0.anyCapability?.isFree != false } + : parsedEntitlements + + return [Components.Schemas.CapabilityType: [Components.Schemas.CapabilitySetting]]( + eligibleEntitlements.compactMap(\.anyCapability).map { ($0.capabilityType, $0.settings ?? []) }, + uniquingKeysWith: { $1 } + ) + } + /// Registers the app with the given entitlements private func upsertApp( bundleID: String, @@ -85,11 +100,7 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation { uniquingKeysWith: { $1 } ) - let wantedCapabilitiesList = try entitlements.entitlements().compactMap(\.anyCapability) - let wantedCapabilities = [Components.Schemas.CapabilityType: [Components.Schemas.CapabilitySetting]]( - wantedCapabilitiesList.map { ($0.capabilityType, $0.settings ?? []) }, - uniquingKeysWith: { $1 } - ) + let wantedCapabilities = try Self.capabilities(for: entitlements, isFreeTeam: isFreeTeam) for (typ, cap) in existingCapabilities { if let wantedSettings = wantedCapabilities[typ] { diff --git a/Sources/XKit/Model/Entitlements/EntitlementTypes.swift b/Sources/XKit/Model/Entitlements/EntitlementTypes.swift index 26224859..69222ac0 100644 --- a/Sources/XKit/Model/Entitlements/EntitlementTypes.swift +++ b/Sources/XKit/Model/Entitlements/EntitlementTypes.swift @@ -17,6 +17,8 @@ extension EntitlementContainer { TeamIdentifierEntitlement.self, KeychainAccessGroupsEntitlement.self, GetTaskAllowEntitlement.self, + // This was disabled in the original Supersign implementation even though + // the entitlement codec and capability mapping were already present. AssociatedDomainsEntitlement.self, APSEnvironmentEntitlement.self, AppGroupEntitlement.self, diff --git a/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift b/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift index af6a8cfa..b49ccd51 100644 --- a/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift +++ b/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift @@ -35,3 +35,29 @@ import Testing #expect(dictionary[AssociatedDomainsEntitlement.identifier] as? [String] == ["applinks:example.com"]) } + +@Test func associatedDomainsEntitlementIsRequestedForPaidTeams() throws { + let entitlements = try Entitlements(entitlements: [ + AssociatedDomainsEntitlement(rawValue: ["applinks:example.com"]) + ]) + + let capabilities = try DeveloperServicesAddAppOperation.capabilities( + for: entitlements, + isFreeTeam: false + ) + + #expect(capabilities[.init(.associatedDomains)] != nil) +} + +@Test func associatedDomainsEntitlementIsFilteredForFreeTeams() throws { + let entitlements = try Entitlements(entitlements: [ + AssociatedDomainsEntitlement(rawValue: ["applinks:example.com"]) + ]) + + let capabilities = try DeveloperServicesAddAppOperation.capabilities( + for: entitlements, + isFreeTeam: true + ) + + #expect(capabilities[.init(.associatedDomains)] == nil) +} From 19635fe5b969949c715ba2299488049d2abdb4e3 Mon Sep 17 00:00:00 2001 From: jdpy19 Date: Thu, 15 Oct 2026 10:08:15 -0400 Subject: [PATCH 3/3] Revert "test: cover associated domains capability filtering" This reverts commit bc1ae2948e29c99b8e91548f01601756696dd402. --- .../DeveloperServicesAddAppOperation.swift | 21 ++++----------- .../Model/Entitlements/EntitlementTypes.swift | 2 -- .../AssociatedDomainsEntitlementTests.swift | 26 ------------------- 3 files changed, 5 insertions(+), 44 deletions(-) diff --git a/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift b/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift index 31da19c6..d726e91e 100644 --- a/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift +++ b/Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift @@ -37,21 +37,6 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation { self.root = root } - static func capabilities( - for entitlements: Entitlements, - isFreeTeam: Bool - ) throws -> [Components.Schemas.CapabilityType: [Components.Schemas.CapabilitySetting]] { - let parsedEntitlements = try entitlements.entitlements() - let eligibleEntitlements = isFreeTeam - ? parsedEntitlements.filter { $0.anyCapability?.isFree != false } - : parsedEntitlements - - return [Components.Schemas.CapabilityType: [Components.Schemas.CapabilitySetting]]( - eligibleEntitlements.compactMap(\.anyCapability).map { ($0.capabilityType, $0.settings ?? []) }, - uniquingKeysWith: { $1 } - ) - } - /// Registers the app with the given entitlements private func upsertApp( bundleID: String, @@ -100,7 +85,11 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation { uniquingKeysWith: { $1 } ) - let wantedCapabilities = try Self.capabilities(for: entitlements, isFreeTeam: isFreeTeam) + let wantedCapabilitiesList = try entitlements.entitlements().compactMap(\.anyCapability) + let wantedCapabilities = [Components.Schemas.CapabilityType: [Components.Schemas.CapabilitySetting]]( + wantedCapabilitiesList.map { ($0.capabilityType, $0.settings ?? []) }, + uniquingKeysWith: { $1 } + ) for (typ, cap) in existingCapabilities { if let wantedSettings = wantedCapabilities[typ] { diff --git a/Sources/XKit/Model/Entitlements/EntitlementTypes.swift b/Sources/XKit/Model/Entitlements/EntitlementTypes.swift index 69222ac0..26224859 100644 --- a/Sources/XKit/Model/Entitlements/EntitlementTypes.swift +++ b/Sources/XKit/Model/Entitlements/EntitlementTypes.swift @@ -17,8 +17,6 @@ extension EntitlementContainer { TeamIdentifierEntitlement.self, KeychainAccessGroupsEntitlement.self, GetTaskAllowEntitlement.self, - // This was disabled in the original Supersign implementation even though - // the entitlement codec and capability mapping were already present. AssociatedDomainsEntitlement.self, APSEnvironmentEntitlement.self, AppGroupEntitlement.self, diff --git a/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift b/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift index b49ccd51..af6a8cfa 100644 --- a/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift +++ b/Tests/XToolTests/AssociatedDomainsEntitlementTests.swift @@ -35,29 +35,3 @@ import Testing #expect(dictionary[AssociatedDomainsEntitlement.identifier] as? [String] == ["applinks:example.com"]) } - -@Test func associatedDomainsEntitlementIsRequestedForPaidTeams() throws { - let entitlements = try Entitlements(entitlements: [ - AssociatedDomainsEntitlement(rawValue: ["applinks:example.com"]) - ]) - - let capabilities = try DeveloperServicesAddAppOperation.capabilities( - for: entitlements, - isFreeTeam: false - ) - - #expect(capabilities[.init(.associatedDomains)] != nil) -} - -@Test func associatedDomainsEntitlementIsFilteredForFreeTeams() throws { - let entitlements = try Entitlements(entitlements: [ - AssociatedDomainsEntitlement(rawValue: ["applinks:example.com"]) - ]) - - let capabilities = try DeveloperServicesAddAppOperation.capabilities( - for: entitlements, - isFreeTeam: true - ) - - #expect(capabilities[.init(.associatedDomains)] == nil) -}