diff --git a/.DS_Store b/.DS_Store index b352f12..c94ca09 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/client/.DS_Store b/client/.DS_Store index b7e1b5a..fb9bb09 100644 Binary files a/client/.DS_Store and b/client/.DS_Store differ diff --git a/client/app.go b/client/app.go index 35e05ca..29aa498 100644 --- a/client/app.go +++ b/client/app.go @@ -124,9 +124,10 @@ type App struct { lastSyncTime time.Time isStreamingSc bool streamScStop chan struct{} - statusMsg string - expectingLogin bool - backendOnline bool + statusMsg string + expectingLogin bool + needsClearPortalStorage bool + backendOnline bool dashboard StudentDashboardSnapshot wifiEnforce bool acceptedWifi map[string]bool @@ -209,6 +210,9 @@ func (a *App) startup(ctx context.Context) { a.loadSession() a.loadStats() + // Request Location Access (macOS) + winapi.RequestLocationAccess() + // Register blocker callbacks blocker.Instance.OnBlocked = func(procName string, title string) { go a.reportBlockedApp(procName, title) @@ -534,7 +538,8 @@ func (a *App) NavigateToLogin() { func (a *App) Logout() { a.mu.Lock() a.student = nil - a.expectingLogin = false + a.expectingLogin = true + a.needsClearPortalStorage = true a.mu.Unlock() _ = os.Remove(a.sessionPath) @@ -552,7 +557,7 @@ func (a *App) Logout() { a.stopScreenshotStream() guard.SuppressFor(5 * time.Second) - runtime.WindowReloadApp(a.ctx) + runtime.WindowExecJS(a.ctx, "window.location.href = 'https://portal.rikkei.edu.vn/dangnhap'") } // GetStats trả về Wifi, thời gian online/offline và trạng thái giám sát @@ -622,9 +627,23 @@ func (a *App) authStorageScanner() { a.mu.Lock() expecting := a.expectingLogin + needsClear := a.needsClearPortalStorage loggedIn := a.student != nil a.mu.Unlock() + if needsClear && expecting { + runtime.WindowExecJS(a.ctx, ` + try { + localStorage.removeItem("student"); + localStorage.removeItem("token"); + } catch(e) {} + `) + a.mu.Lock() + a.needsClearPortalStorage = false + a.mu.Unlock() + continue + } + if loggedIn || !expecting { continue } @@ -661,8 +680,16 @@ func (a *App) runMonitorTick() { wifi := winapi.GetWifiConnection() a.mu.Lock() - a.wifiSSID = wifi.SSID - a.wifiBSSID = wifi.BSSID + if strings.Contains(strings.ToLower(wifi.SSID), "redacted") { + a.wifiSSID = "Chưa cấp quyền Vị Trí (macOS)" + } else { + a.wifiSSID = wifi.SSID + } + if strings.Contains(strings.ToLower(wifi.BSSID), "redacted") { + a.wifiBSSID = "Chưa cấp quyền Vị Trí (macOS)" + } else { + a.wifiBSSID = wifi.BSSID + } a.mu.Unlock() backendOnline := a.pingBackend() @@ -732,8 +759,16 @@ func (a *App) refreshNetworkStatus() { wifi := winapi.GetWifiConnection() backendOnline := a.pingBackend() a.mu.Lock() - a.wifiSSID = wifi.SSID - a.wifiBSSID = wifi.BSSID + if strings.Contains(strings.ToLower(wifi.SSID), "redacted") { + a.wifiSSID = "Chưa cấp quyền Vị Trí (macOS)" + } else { + a.wifiSSID = wifi.SSID + } + if strings.Contains(strings.ToLower(wifi.BSSID), "redacted") { + a.wifiBSSID = "Chưa cấp quyền Vị Trí (macOS)" + } else { + a.wifiBSSID = wifi.BSSID + } a.backendOnline = backendOnline a.mu.Unlock() if backendOnline { @@ -923,6 +958,10 @@ func (a *App) isWifiAllowed(ssid, bssid string) bool { if !enforce || len(allowed) == 0 { return true } + // Bypass Wi-Fi check if it's redacted by macOS privacy controls + if strings.Contains(strings.ToLower(ssid), "redacted") || strings.Contains(strings.ToLower(bssid), "redacted") { + return true + } bKey := winapi.BSSIDKey(bssid) if bKey == "" || len(bKey) != 12 { return false diff --git a/client/build/.DS_Store b/client/build/.DS_Store index d680716..7b16691 100644 Binary files a/client/build/.DS_Store and b/client/build/.DS_Store differ diff --git a/client/build/darwin/Info.dev.plist b/client/build/darwin/Info.dev.plist index 14121ef..8496abb 100644 --- a/client/build/darwin/Info.dev.plist +++ b/client/build/darwin/Info.dev.plist @@ -64,5 +64,7 @@ NSAllowsLocalNetworking + NSLocationWhenInUseUsageDescription + Ứng dụng cần quyền vị trí để xác thực mạng Wi-Fi phòng thi. diff --git a/client/build/darwin/Info.plist b/client/build/darwin/Info.plist index d17a747..a4698d6 100644 --- a/client/build/darwin/Info.plist +++ b/client/build/darwin/Info.plist @@ -59,5 +59,7 @@ {{end}} {{end}} + NSLocationWhenInUseUsageDescription + Ứng dụng cần quyền vị trí để xác thực mạng Wi-Fi phòng thi. diff --git a/client/internal/winapi/wifi_darwin.go b/client/internal/winapi/wifi_darwin.go index 1f5a3af..21cea90 100644 --- a/client/internal/winapi/wifi_darwin.go +++ b/client/internal/winapi/wifi_darwin.go @@ -2,45 +2,52 @@ package winapi +/* +#cgo LDFLAGS: -framework CoreWLAN -framework CoreLocation -framework Foundation +#include + +typedef struct { + char* ssid; + char* bssid; +} CWifiInfo; + +void RequestLocationPermission(); +CWifiInfo GetCurrentWifiInfo(); +*/ +import "C" import ( - "bytes" - "os/exec" - "strings" + "unsafe" ) -// GetWifiConnection đọc SSID và BSSID từ ipconfig (macOS) -func GetWifiConnection() WifiConnection { - // Find Wi-Fi interface dynamically - wifiInterface := "en0" - cmdPort := exec.Command("networksetup", "-listallhardwareports") - if outPort, err := cmdPort.Output(); err == nil { - lines := strings.Split(string(outPort), "\n") - for i := 0; i < len(lines)-1; i++ { - if strings.Contains(lines[i], "Hardware Port: Wi-Fi") { - next := strings.TrimSpace(lines[i+1]) - if strings.HasPrefix(next, "Device:") { - wifiInterface = strings.TrimSpace(strings.TrimPrefix(next, "Device:")) - break - } - } - } - } - - cmd := exec.Command("ipconfig", "getsummary", wifiInterface) - var out bytes.Buffer - cmd.Stdout = &out - if err := cmd.Run(); err != nil { - return WifiConnection{} - } - - var conn WifiConnection - for _, line := range strings.Split(out.String(), "\n") { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "SSID :") { - conn.SSID = strings.TrimSpace(strings.TrimPrefix(trimmed, "SSID :")) - } else if strings.HasPrefix(trimmed, "BSSID :") { - conn.BSSID = normalizeMAC(strings.TrimSpace(strings.TrimPrefix(trimmed, "BSSID :"))) - } - } - return conn +// RequestLocationAccess requests Location permission on macOS +func RequestLocationAccess() { + C.RequestLocationPermission() +} + +// GetWifiConnection đọc SSID và BSSID từ CoreWLAN (macOS) +func GetWifiConnection() WifiConnection { + info := C.GetCurrentWifiInfo() + defer func() { + if info.ssid != nil { + C.free(unsafe.Pointer(info.ssid)) + } + if info.bssid != nil { + C.free(unsafe.Pointer(info.bssid)) + } + }() + + ssid := "" + if info.ssid != nil { + ssid = C.GoString(info.ssid) + } + + bssid := "" + if info.bssid != nil { + bssid = C.GoString(info.bssid) + } + + return WifiConnection{ + SSID: ssid, + BSSID: normalizeMAC(bssid), + } } diff --git a/client/internal/winapi/wifi_darwin.m b/client/internal/winapi/wifi_darwin.m new file mode 100644 index 0000000..35e8e03 --- /dev/null +++ b/client/internal/winapi/wifi_darwin.m @@ -0,0 +1,45 @@ +#import +#import +#import + +static CLLocationManager *locationManager = nil; + +void RequestLocationPermission() { + dispatch_async(dispatch_get_main_queue(), ^{ + if (locationManager == nil) { + locationManager = [[CLLocationManager alloc] init]; + } + if (@available(macOS 10.15, *)) { + [locationManager requestWhenInUseAuthorization]; + } + }); +} + +typedef struct { + char* ssid; + char* bssid; +} CWifiInfo; + +CWifiInfo GetCurrentWifiInfo() { + CWifiInfo info; + info.ssid = NULL; + info.bssid = NULL; + + @autoreleasepool { + CWWiFiClient *client = [CWWiFiClient sharedWiFiClient]; + if (client != nil) { + CWInterface *interface = [client interface]; + if (interface != nil) { + NSString *ssidStr = [interface ssid]; + NSString *bssidStr = [interface bssid]; + if (ssidStr != nil) { + info.ssid = strdup([ssidStr UTF8String]); + } + if (bssidStr != nil) { + info.bssid = strdup([bssidStr UTF8String]); + } + } + } + } + return info; +} diff --git a/client/internal/winapi/wifi_other.go b/client/internal/winapi/wifi_other.go index 550643c..f823f62 100644 --- a/client/internal/winapi/wifi_other.go +++ b/client/internal/winapi/wifi_other.go @@ -6,3 +6,6 @@ package winapi func GetWifiConnection() WifiConnection { return WifiConnection{} } + +// RequestLocationAccess requests Location permission (stub for other systems) +func RequestLocationAccess() {} diff --git a/client/internal/winapi/wifi_windows.go b/client/internal/winapi/wifi_windows.go index a7e6bf0..4400976 100644 --- a/client/internal/winapi/wifi_windows.go +++ b/client/internal/winapi/wifi_windows.go @@ -38,3 +38,6 @@ func GetWifiConnection() WifiConnection { } return conn } + +// RequestLocationAccess requests Location permission (stub for Windows) +func RequestLocationAccess() {}