fix(magic-mac): macOS 26 profile install via NSWorkspace + de-dup register card

Zwei Bugs:

1) 'profiles install -path' wurde mit macOS 15+ entfernt
   ('profiles tool no longer supports installs. Use System Settings
   Profiles to add configuration profiles.'). Auf macOS 26 (Tahoe)
   ist das Hard-Removal.
   -> Switch zu NSWorkspace.shared.open(profileURL): \u00f6ffnet die
   .mobileconfig in System Settings -> Profile-Pane. User best\u00e4tigt
   manuell + gibt Admin-PW. Einziger Weg ohne MDM-Enrollment.
   -> success-Text passt: 'Bitte in System Settings Installieren
   klicken'.

2) Doppelte 'Mac registriert'-Karte: successMessage-Card UND
   strukturierte Registration-Status-Card beide sichtbar nach
   register. Auto-Profile-Install nach Register war eh totes
   Verhalten (DNS jetzt optional).
   -> successMessage wird nicht mehr in handleRegistration gesetzt,
   nur noch in handleProfileInstall. Eine Karte.
This commit is contained in:
chahinebrini 2026-06-03 10:29:30 +02:00
parent 18c3a49404
commit 87d6395ed2
2 changed files with 27 additions and 23 deletions

View File

@ -1,4 +1,5 @@
import Foundation
import AppKit
/// Service für Mac-DNS-Profile-Download + Installation.
enum MacProfileInstaller {
@ -20,8 +21,12 @@ enum MacProfileInstaller {
}
}
/// Lädt Mac-DNS-Profile von Backend und installiert via `profiles install`.
/// Profile-File wird nach Installation gelöscht (enthält sensiblen Token).
/// Lädt Mac-DNS-Profile von Backend und öffnet es in System Settings Profiles.
/// Ab macOS 15+ ist `profiles install` für Configuration Profiles entfernt
/// ("profiles tool no longer supports installs. Use System Settings
/// Profiles to add configuration profiles."). Einzig zulässiger Weg ohne
/// MDM-Enrollment: NSWorkspace öffnet die .mobileconfig Profiles-Pane
/// erscheint User muss manuell Installieren" klicken + Admin-PW geben.
static func downloadAndInstall(registration: MagicRegistration) async throws {
// 1. Download profile
let profileURL: URL
@ -31,19 +36,20 @@ enum MacProfileInstaller {
throw InstallerError.downloadFailed(error.localizedDescription)
}
// 2. Install via `profiles` command (macOS-only)
let result = try await ProcessRunner.run(
"/usr/bin/profiles",
arguments: ["install", "-path", profileURL.path]
)
// 3. Clean up downloaded file
try? FileManager.default.removeItem(at: profileURL)
if result.exitCode != 0 {
let errorMsg = result.stderr.isEmpty ? result.stdout : result.stderr
throw InstallerError.installFailed(errorMsg)
// 2. Open in System Settings Profiles (user must confirm in UI)
let opened = await MainActor.run {
NSWorkspace.shared.open(profileURL)
}
if !opened {
try? FileManager.default.removeItem(at: profileURL)
throw InstallerError.installFailed(
"System Settings konnte das Profil nicht öffnen. Datei liegt unter: \(profileURL.path)"
)
}
// NICHT löschen System Settings braucht die Datei evtl. noch.
// OS räumt /tmp selbst auf.
}
/// Prüft ob ReBreak-DNS-Profile bereits installiert ist.

View File

@ -196,13 +196,10 @@ struct MacRegistrationView: View {
try await model.registerMac()
await MainActor.run {
successMessage = "Mac erfolgreich registriert ✓"
isRegistering = false
}
// Auto-trigger profile install
try await Task.sleep(nanoseconds: 500_000_000) // 0.5s delay
await handleProfileInstall()
// KEIN Auto-Profile-Install mehr DNS-Schutz ist optional.
// User entscheidet selbst via Button.
} catch {
await MainActor.run {
@ -226,14 +223,15 @@ struct MacRegistrationView: View {
do {
try await MacProfileInstaller.downloadAndInstall(registration: registration)
// Re-check profile status
await checkProfileStatus()
await MainActor.run {
isInstallingProfile = false
successMessage = "DNS-Filter-Profil installiert ✓"
successMessage = "System Settings → Profile geöffnet. Bitte dort „Installieren" klicken und Admin-Passwort eingeben."
}
// Re-check profile status nach kurzer Wartezeit (User muss in UI bestätigen)
try? await Task.sleep(nanoseconds: 3_000_000_000)
await checkProfileStatus()
} catch {
await MainActor.run {
isInstallingProfile = false