Browser enrollment used osascript on every elevate. Ship a signed privileged helper installed at PKG time so probo:// can enroll over XPC with no second admin prompt. Add make install/uninstall/clean for local PKG test loops, and show alerts only on failure. Mirror the Go lint path for the macOS SPM package: Make targets, root configs, and a Linux CI job. Keep checks syntax-only so they do not need a macOS SDK. Format the existing sources so the new gates start clean. Signed-off-by: Ludovic Vielle <ludovic@probo.com>
104 lines
3.4 KiB
Swift
104 lines
3.4 KiB
Swift
import Foundation
|
|
import ProboAgentShared
|
|
import os
|
|
|
|
final class Helper: NSObject, ProboAgentHelperProtocol, NSXPCListenerDelegate {
|
|
private static let log = Logger(
|
|
subsystem: "com.probo.agent.helper",
|
|
category: "Helper"
|
|
)
|
|
|
|
func getVersion(withReply reply: @escaping (String) -> Void) {
|
|
reply(ProboAgentHelperConstants.helperVersion)
|
|
}
|
|
|
|
func ping(withReply reply: @escaping (Bool) -> Void) {
|
|
Self.log.info("ping")
|
|
reply(true)
|
|
}
|
|
|
|
func install(
|
|
serverURL: String,
|
|
enrollmentToken: String,
|
|
configDir: String,
|
|
withReply reply: @escaping (Int32, String?) -> Void
|
|
) {
|
|
let trimmedServer = serverURL.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let trimmedToken = enrollmentToken.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let dir = configDir.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
guard !trimmedServer.isEmpty, !trimmedToken.isEmpty else {
|
|
reply(1, "server URL and enrollment token are required")
|
|
return
|
|
}
|
|
|
|
// Pass the token via env rather than argv so it does not show up in
|
|
// process listings (ps / Activity Monitor). Install already accepts
|
|
// PROBO_ENROLLMENT_TOKEN when --enrollment-token is omitted.
|
|
var args = [
|
|
"install",
|
|
"--server", trimmedServer,
|
|
]
|
|
if !dir.isEmpty {
|
|
args.append(contentsOf: ["--dir", dir])
|
|
}
|
|
|
|
var environment = ProcessInfo.processInfo.environment
|
|
environment["PROBO_ENROLLMENT_TOKEN"] = trimmedToken
|
|
|
|
let result = runAgent(args: args, environment: environment)
|
|
reply(result.exitCode, result.output)
|
|
}
|
|
|
|
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection)
|
|
-> Bool
|
|
{
|
|
guard ClientAuth.accepts(connection: connection) else {
|
|
Self.log.error(
|
|
"refused XPC connection pid=\(connection.processIdentifier, privacy: .public)")
|
|
return false
|
|
}
|
|
|
|
Self.log.info(
|
|
"accepted XPC connection pid=\(connection.processIdentifier, privacy: .public)")
|
|
connection.exportedInterface = NSXPCInterface(with: ProboAgentHelperProtocol.self)
|
|
connection.exportedObject = self
|
|
connection.resume()
|
|
return true
|
|
}
|
|
|
|
private struct CommandResult {
|
|
let exitCode: Int32
|
|
let output: String?
|
|
}
|
|
|
|
private func runAgent(args: [String], environment: [String: String]? = nil) -> CommandResult {
|
|
let process = Process()
|
|
process.executableURL = URL(fileURLWithPath: ProboAgentHelperConstants.agentExecutablePath)
|
|
process.arguments = args
|
|
if let environment {
|
|
process.environment = environment
|
|
}
|
|
|
|
let pipe = Pipe()
|
|
process.standardOutput = pipe
|
|
process.standardError = pipe
|
|
|
|
do {
|
|
try process.run()
|
|
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
|
|
process.waitUntilExit()
|
|
let text = String(data: data, encoding: .utf8)?
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
return CommandResult(
|
|
exitCode: process.terminationStatus,
|
|
output: text?.isEmpty == false ? text : nil
|
|
)
|
|
} catch {
|
|
return CommandResult(exitCode: 1, output: error.localizedDescription)
|
|
}
|
|
}
|
|
|
|
}
|