krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

main: DomainDig/OwnerAccess.swift · raw

 1import CloudKit
 2
 3/// Owner-only entitlement support. The app owner is identified by their CloudKit
 4/// user-record ID  a stable, opaque per-Apple-ID value for this app's container.
 5/// `PurchaseService` grants the owner Pro+ when the signed-in iCloud user matches,
 6/// so the owner does not need a purchase.
 7///
 8/// Publishing the record ID here is safe: it is not an Apple ID or any personal
 9/// identifier, it is scoped to the `iCloud.net.cleberg.DomainDig` container, and
10/// CloudKit identity is verified server-side  another user cannot present it as
11/// their own. An empty value makes the allowlist inert.
12enum OwnerAccess {
13    static let ownerUserRecordID = "_1c35d6a25540b3ef00023cc0425ec373"
14
15    static var isConfigured: Bool { !ownerUserRecordID.isEmpty }
16
17    /// Whether this build actually carries its entitlements.
18    ///
19    /// Touching CloudKit without the iCloud container entitlement does not
20    /// return an error  it raises an Objective-C exception from inside a
21    /// `dispatch_once`, which Swift cannot catch, so the process aborts before
22    /// the first screen draws. That is what any unsigned build does, including
23    /// CI: `xcodebuild ... CODE_SIGNING_ALLOWED=NO` embeds no entitlements.
24    ///
25    /// The App Group is declared in the same entitlements file and is stripped
26    /// by the same mechanism, but asking for its container returns nil rather
27    /// than raising. So it answers the question CloudKit will not: does this
28    /// process have its entitlements at all?
29    private static var hasEntitlements: Bool {
30        FileManager.default.containerURL(
31            forSecurityApplicationGroupIdentifier: DomainDigWidgetStore.appGroupID
32        ) != nil
33    }
34
35    /// The current iCloud user's record name for this app's container, or nil if
36    /// it is unavailable (not signed into iCloud, restricted, offline before the
37    /// first fetch, or running from a build without entitlements).
38    static func currentUserRecordName() async -> String? {
39        guard hasEntitlements else { return nil }
40        do {
41            return try await CKContainer.default().userRecordID().recordName
42        } catch {
43            return nil
44        }
45    }
46
47    /// True only when the allowlist is configured and the current iCloud user is
48    /// the owner.
49    static func isOwner() async -> Bool {
50        guard isConfigured else { return false }
51        return await currentUserRecordName() == ownerUserRecordID
52    }
53}