mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 11:39:57 +00:00
[client] Add autostart preference marker and MDM disableAutostart key
Adds the autostartInitialized marker to the Wails UI preferences store so the one-time autostart default decision can persist per OS user, and a UI-only disableAutostart MDM policy key that suppresses the default and flows into GetConfigResponse.mDMManagedFields like disableAutoConnect.
This commit is contained in:
@@ -22,6 +22,7 @@ var allKeys = []string{
|
||||
KeyDisableMetricsCollection,
|
||||
KeyAllowServerSSH,
|
||||
KeyDisableAutoConnect,
|
||||
KeyDisableAutostart,
|
||||
KeyPreSharedKey,
|
||||
KeyRosenpassEnabled,
|
||||
KeyRosenpassPermissive,
|
||||
|
||||
@@ -20,10 +20,10 @@ import (
|
||||
// names (lowerCamelCase) so the daemon can map a Policy key directly to a
|
||||
// configuration field.
|
||||
const (
|
||||
KeyManagementURL = "managementURL"
|
||||
KeyDisableUpdateSettings = "disableUpdateSettings"
|
||||
KeyDisableProfiles = "disableProfiles"
|
||||
KeyDisableNetworks = "disableNetworks"
|
||||
KeyManagementURL = "managementURL"
|
||||
KeyDisableUpdateSettings = "disableUpdateSettings"
|
||||
KeyDisableProfiles = "disableProfiles"
|
||||
KeyDisableNetworks = "disableNetworks"
|
||||
// KeyDisableAdvancedView gates the advanced-view section in the
|
||||
// upcoming UI revision. UI-only: NOT stored on Config, not
|
||||
// applied by applyMDMPolicy, not rejectable via SetConfig. The
|
||||
@@ -37,10 +37,16 @@ const (
|
||||
KeyDisableMetricsCollection = "disableMetricsCollection"
|
||||
KeyAllowServerSSH = "allowServerSSH"
|
||||
KeyDisableAutoConnect = "disableAutoConnect"
|
||||
KeyPreSharedKey = "preSharedKey"
|
||||
KeyRosenpassEnabled = "rosenpassEnabled"
|
||||
KeyRosenpassPermissive = "rosenpassPermissive"
|
||||
KeyWireguardPort = "wireguardPort"
|
||||
// KeyDisableAutostart suppresses the GUI's fresh-install
|
||||
// launch-on-login default and marks the Settings toggle as
|
||||
// MDM-managed. UI-only: NOT stored on Config and not applied by
|
||||
// applyMDMPolicy; the GUI reads it directly and it appears in
|
||||
// GetConfigResponse.mDMManagedFields when set.
|
||||
KeyDisableAutostart = "disableAutostart"
|
||||
KeyPreSharedKey = "preSharedKey"
|
||||
KeyRosenpassEnabled = "rosenpassEnabled"
|
||||
KeyRosenpassPermissive = "rosenpassPermissive"
|
||||
KeyWireguardPort = "wireguardPort"
|
||||
|
||||
// Split tunnel is modeled as a single conceptual policy with two
|
||||
// registry/plist values. KeySplitTunnelMode is the discriminator
|
||||
|
||||
@@ -54,6 +54,10 @@ type UIPreferences struct {
|
||||
Language i18n.LanguageCode `json:"language"`
|
||||
ViewMode ViewMode `json:"viewMode"`
|
||||
OnboardingCompleted bool `json:"onboardingCompleted"`
|
||||
// AutostartInitialized records that the one-time autostart default
|
||||
// decision has run for this OS user. It only ever transitions to true
|
||||
// and is never reset, so the default-on flow runs at most once, ever.
|
||||
AutostartInitialized bool `json:"autostartInitialized"`
|
||||
}
|
||||
|
||||
// LanguageValidator rejects SetLanguage inputs with no shipped bundle.
|
||||
@@ -157,6 +161,27 @@ func (s *Store) SetOnboardingCompleted(done bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAutostartInitialized persists the one-time autostart decision marker.
|
||||
// No-op if unchanged.
|
||||
func (s *Store) SetAutostartInitialized(done bool) error {
|
||||
s.mu.Lock()
|
||||
if s.current.AutostartInitialized == done {
|
||||
s.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
next := s.current
|
||||
next.AutostartInitialized = done
|
||||
if err := s.persistLocked(next); err != nil {
|
||||
s.mu.Unlock()
|
||||
return fmt.Errorf("persist preferences: %w", err)
|
||||
}
|
||||
s.current = next
|
||||
s.mu.Unlock()
|
||||
|
||||
s.broadcast(next)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLanguage validates, persists, and broadcasts. No-op if unchanged.
|
||||
func (s *Store) SetLanguage(lang i18n.LanguageCode) error {
|
||||
if lang == "" {
|
||||
|
||||
@@ -215,6 +215,29 @@ func TestStore_FileShapeIsJSON(t *testing.T) {
|
||||
assert.Equal(t, i18n.LanguageCode("hu"), parsed.Language)
|
||||
}
|
||||
|
||||
func TestStore_SetAutostartInitializedPersistsAcrossReload(t *testing.T) {
|
||||
withTempConfigDir(t)
|
||||
emitter := &recordingEmitter{}
|
||||
s, err := NewStore(nil, emitter)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, s.Get().AutostartInitialized, "marker must default to false when no file is on disk")
|
||||
|
||||
require.NoError(t, s.SetAutostartInitialized(true))
|
||||
assert.True(t, s.Get().AutostartInitialized, "Get should reflect the persisted marker")
|
||||
require.Len(t, emitter.calledWith(EventPreferencesChanged), 1, "first marker write should broadcast")
|
||||
|
||||
// Re-setting the same value must be a no-op: no disk write, no broadcast.
|
||||
require.NoError(t, s.SetAutostartInitialized(true))
|
||||
assert.Len(t, emitter.calledWith(EventPreferencesChanged), 1, "idempotent marker write should not broadcast again")
|
||||
|
||||
// A fresh Store (new GUI launch) must see the marker so the autostart
|
||||
// default decision never runs twice.
|
||||
reloaded, err := NewStore(nil, nil)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, reloaded.Get().AutostartInitialized, "marker must survive a reload from disk")
|
||||
}
|
||||
|
||||
func TestStore_ErrUnsupportedSentinel(t *testing.T) {
|
||||
// Verifies callers can match on the sentinel error rather than parsing
|
||||
// strings — protects against accidental %v -> %w changes that would
|
||||
|
||||
@@ -20,11 +20,12 @@ type MDMFields struct {
|
||||
DisableServerRoutes bool `json:"disableServerRoutes"`
|
||||
AllowServerSSH *bool `json:"allowServerSSH"`
|
||||
DisableAutoConnect bool `json:"disableAutoConnect"`
|
||||
DisableAutostart bool `json:"disableAutostart"`
|
||||
BlockInbound bool `json:"blockInbound"`
|
||||
DisableMetricsCollection bool `json:"disableMetricsCollection"`
|
||||
SplitTunnelMode bool `json:"splitTunnelMode"`
|
||||
SplitTunnelApps bool `json:"splitTunnelApps"`
|
||||
DisableAdvancedView bool `json:"disableAdvancedView"`
|
||||
DisableAdvancedView bool `json:"disableAdvancedView"`
|
||||
}
|
||||
|
||||
type Features struct {
|
||||
|
||||
Reference in New Issue
Block a user