mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-15 11:19:08 +02:00
CI fixes
- Add docstrings to `mdm_integration` - refactor for cognitive complexity - mod tidy
This commit is contained in:
+25
-8
@@ -96,7 +96,9 @@ type Policy struct {
|
||||
}
|
||||
|
||||
// NewPolicy constructs a Policy from a key→value map. Pass nil or an empty
|
||||
// map to construct an empty (no-enforcement) Policy.
|
||||
// NewPolicy constructs a Policy backed by the provided key→value map.
|
||||
// If values is nil it is replaced with an empty map so the returned *Policy
|
||||
// is always non-nil and represents no active MDM enforcement when empty.
|
||||
func NewPolicy(values map[string]any) *Policy {
|
||||
if values == nil {
|
||||
values = map[string]any{}
|
||||
@@ -111,7 +113,9 @@ func NewPolicy(values map[string]any) *Policy {
|
||||
// Diagnostic logging differentiates the three states:
|
||||
// - source absent / unsupported platform: trace log only
|
||||
// - source present, zero keys: info "MDM enrolled (no managed keys)"
|
||||
// - source present, N keys: info "MDM enrolled with N managed keys: [...]"
|
||||
// LoadPolicy loads MDM-managed configuration from the platform and returns a Policy representing the managed settings.
|
||||
// If the platform loader fails or returns nil, LoadPolicy returns a non-nil empty Policy.
|
||||
// When the loaded map contains zero keys it logs that MDM is enrolled with no managed keys; when it contains keys it logs the count and a stable, sorted list of key names.
|
||||
func LoadPolicy() *Policy {
|
||||
values, err := loadPlatformPolicy()
|
||||
if err != nil {
|
||||
@@ -169,6 +173,19 @@ func (p *Policy) GetString(key string) (string, bool) {
|
||||
return s, true
|
||||
}
|
||||
|
||||
// boolStringLiterals enumerates the textual boolean encodings the
|
||||
// platform loaders may produce (Windows REG_SZ "true", iOS / Android
|
||||
// managed-config booleans-as-strings, etc.). Lookup keeps GetBool flat
|
||||
// (no nested switch on the string case).
|
||||
var boolStringLiterals = map[string]bool{
|
||||
"true": true,
|
||||
"1": true,
|
||||
"yes": true,
|
||||
"false": false,
|
||||
"0": false,
|
||||
"no": false,
|
||||
}
|
||||
|
||||
// GetBool returns the managed value for key coerced to bool, and whether the
|
||||
// key was set. Accepts native bool and string literals "true"/"false"/"1"/"0".
|
||||
func (p *Policy) GetBool(key string) (bool, bool) {
|
||||
@@ -183,12 +200,8 @@ func (p *Policy) GetBool(key string) (bool, bool) {
|
||||
case bool:
|
||||
return t, true
|
||||
case string:
|
||||
switch t {
|
||||
case "true", "1", "yes":
|
||||
return true, true
|
||||
case "false", "0", "no":
|
||||
return false, true
|
||||
}
|
||||
b, known := boolStringLiterals[t]
|
||||
return b, known
|
||||
case int:
|
||||
return t != 0, true
|
||||
case int64:
|
||||
@@ -257,6 +270,10 @@ func (p *Policy) GetStringSlice(key string) ([]string, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// sortedKeys returns the keys of m as a deterministic, lexicographically
|
||||
// sorted slice. Used internally by Policy.ManagedKeys and LoadPolicy's
|
||||
// diagnostic log line so callers see a stable key order across runs
|
||||
// It produces a deterministic ordering for a map regardless of Go's randomized iteration.
|
||||
func sortedKeys(m map[string]any) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
|
||||
@@ -37,7 +37,11 @@ const policyPlistPath = "/Library/Managed Preferences/io.netbird.client.plist"
|
||||
// map naturally onto the Policy accessor expectations (GetString /
|
||||
// GetBool / GetInt / GetStringSlice). Unknown top-level keys are
|
||||
// logged and skipped so a stray entry in the payload does not block
|
||||
// startup.
|
||||
// loadPlatformPolicy reads the managed-preferences plist at policyPlistPath and returns recognised MDM key/value pairs.
|
||||
//
|
||||
// If the plist file does not exist, it returns (nil, nil). It returns a wrapped error on open/stat/decode failures.
|
||||
// The function refuses to read a world-writable plist and returns an error in that case.
|
||||
// Top-level plist keys are canonicalized (case-insensitive) to the internal MDM key names; unknown keys are logged and skipped.
|
||||
func loadPlatformPolicy() (map[string]any, error) {
|
||||
f, err := os.Open(policyPlistPath)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,8 @@ package mdm
|
||||
// Kotlin/Java on Android) reads the OS managed-config store and pushes the
|
||||
// resulting dictionary in-process via a gomobile entry point that lands in
|
||||
// Phase 5 / Phase 6. The stub keeps the package compilable for mobile
|
||||
// build targets.
|
||||
// loadPlatformPolicy is a stub used on mobile (iOS/Android) builds that returns a nil policy map and no error.
|
||||
// The actual managed-config policy is supplied by the native platform layer.
|
||||
func loadPlatformPolicy() (map[string]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ package mdm
|
||||
|
||||
// loadPlatformPolicy returns no policy on platforms without an MDM channel
|
||||
// (Linux, FreeBSD). MDM enforcement is off and the client behaves as if
|
||||
// the feature did not exist.
|
||||
// loadPlatformPolicy reports that no platform MDM policy is available on non-Windows/Darwin/iOS/Android builds.
|
||||
// It returns a nil policy map and a nil error to indicate MDM enforcement is not present on this platform.
|
||||
func loadPlatformPolicy() (map[string]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,44 @@ const policyRegistryPath = `Software\Policies\NetBird`
|
||||
// - REG_MULTI_SZ -> []string
|
||||
//
|
||||
// Unsupported value types (REG_BINARY, REG_NONE, ...) are skipped with a
|
||||
// warning so a malformed deployment does not block startup.
|
||||
// loadPlatformPolicy reads managed NetBird policy values from HKLM\Software\Policies\NetBird.
|
||||
// If the registry key does not exist it returns (nil, nil).
|
||||
// It returns a map whose keys are canonical policy names and whose values are coerced from registry types:
|
||||
// REG_SZ/REG_EXPAND_SZ -> string, REG_DWORD/REG_QWORD -> int64, REG_MULTI_SZ -> []string.
|
||||
// Unknown value names, unsupported value types, and per-value read errors are skipped and logged; failures opening the key or enumerating values are returned as errors.
|
||||
func readRegistryValue(k registry.Key, name, canonical string, out map[string]any) {
|
||||
_, valType, err := k.GetValue(name, nil)
|
||||
if err != nil {
|
||||
log.Warnf("MDM stat %s\\%s: %v", policyRegistryPath, name, err)
|
||||
return
|
||||
}
|
||||
switch valType {
|
||||
case registry.SZ, registry.EXPAND_SZ:
|
||||
if v, _, err := k.GetStringValue(name); err == nil {
|
||||
out[canonical] = v
|
||||
} else {
|
||||
log.Warnf("MDM read string %s\\%s: %v", policyRegistryPath, name, err)
|
||||
}
|
||||
case registry.DWORD, registry.QWORD:
|
||||
if v, _, err := k.GetIntegerValue(name); err == nil {
|
||||
// uint64 from the registry API; Policy.GetBool / GetInt
|
||||
// helpers consume int64, so narrow safely.
|
||||
out[canonical] = int64(v)
|
||||
} else {
|
||||
log.Warnf("MDM read int %s\\%s: %v", policyRegistryPath, name, err)
|
||||
}
|
||||
case registry.MULTI_SZ:
|
||||
if v, _, err := k.GetStringsValue(name); err == nil {
|
||||
out[canonical] = v
|
||||
} else {
|
||||
log.Warnf("MDM read multi-string %s\\%s: %v", policyRegistryPath, name, err)
|
||||
}
|
||||
default:
|
||||
log.Warnf("MDM ignoring unsupported registry value type %d at %s\\%s",
|
||||
valType, policyRegistryPath, name)
|
||||
}
|
||||
}
|
||||
|
||||
func loadPlatformPolicy() (map[string]any, error) {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, policyRegistryPath, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
@@ -63,37 +100,7 @@ func loadPlatformPolicy() (map[string]any, error) {
|
||||
log.Warnf("MDM ignoring unknown registry value %s\\%s", policyRegistryPath, name)
|
||||
continue
|
||||
}
|
||||
|
||||
_, valType, err := k.GetValue(name, nil)
|
||||
if err != nil {
|
||||
log.Warnf("MDM stat %s\\%s: %v", policyRegistryPath, name, err)
|
||||
continue
|
||||
}
|
||||
switch valType {
|
||||
case registry.SZ, registry.EXPAND_SZ:
|
||||
if v, _, err := k.GetStringValue(name); err == nil {
|
||||
out[canonical] = v
|
||||
} else {
|
||||
log.Warnf("MDM read string %s\\%s: %v", policyRegistryPath, name, err)
|
||||
}
|
||||
case registry.DWORD, registry.QWORD:
|
||||
if v, _, err := k.GetIntegerValue(name); err == nil {
|
||||
// uint64 from the registry API; Policy.GetBool / GetInt
|
||||
// helpers consume int64, so narrow safely.
|
||||
out[canonical] = int64(v)
|
||||
} else {
|
||||
log.Warnf("MDM read int %s\\%s: %v", policyRegistryPath, name, err)
|
||||
}
|
||||
case registry.MULTI_SZ:
|
||||
if v, _, err := k.GetStringsValue(name); err == nil {
|
||||
out[canonical] = v
|
||||
} else {
|
||||
log.Warnf("MDM read multi-string %s\\%s: %v", policyRegistryPath, name, err)
|
||||
}
|
||||
default:
|
||||
log.Warnf("MDM ignoring unsupported registry value type %d at %s\\%s",
|
||||
valType, policyRegistryPath, name)
|
||||
}
|
||||
readRegistryValue(k, name, canonical, out)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
+14
-5
@@ -25,7 +25,8 @@ const testReloadInterval = 1 * time.Second
|
||||
|
||||
// reloadInterval returns the production cadence, or the accelerated test
|
||||
// cadence when running under `go test`. Centralising the choice here keeps
|
||||
// the prod/test split in one place and out of the ticker's call sites.
|
||||
// reloadInterval selects the polling interval used to re-read the OS-native MDM policy.
|
||||
// It returns testReloadInterval when tests are running (testing.Testing() == true) and defaultReloadInterval otherwise.
|
||||
func reloadInterval() time.Duration {
|
||||
if testing.Testing() {
|
||||
return testReloadInterval
|
||||
@@ -52,7 +53,9 @@ type Ticker struct {
|
||||
// NewTicker constructs a Ticker that re-reads the OS-native policy every
|
||||
// reloadInterval() and invokes onChange on any diff. The cadence is owned by
|
||||
// reloadInterval (production default, accelerated under `go test`); callers
|
||||
// do not supply it. onChange may be nil for a log-only ticker.
|
||||
// NewTicker creates a Ticker that polls the OS-native MDM policy at the package reload interval and invokes onChange when a policy change is detected.
|
||||
// If onChange is nil the ticker will only log detected changes.
|
||||
// The ticker's initial snapshot is populated by loading the current policy.
|
||||
func NewTicker(onChange func(prev, curr *Policy)) *Ticker {
|
||||
return &Ticker{
|
||||
interval: reloadInterval(),
|
||||
@@ -91,7 +94,7 @@ func (t *Ticker) Run(ctx context.Context) {
|
||||
}
|
||||
|
||||
// PoliciesEqual reports whether two Policy instances carry the same managed
|
||||
// key set with identical values. Nil and empty policies compare equal.
|
||||
// value maps for deep equality.
|
||||
func PoliciesEqual(a, b *Policy) bool {
|
||||
if a.IsEmpty() && b.IsEmpty() {
|
||||
return true
|
||||
@@ -103,7 +106,11 @@ func PoliciesEqual(a, b *Policy) bool {
|
||||
}
|
||||
|
||||
// diffPolicies returns the keys added in curr, removed from prev, and whose
|
||||
// value changed. Returned slices are sorted for stable log output.
|
||||
// diffPolicies reports keys that were added, removed, or changed between two policies.
|
||||
// The returned slices contain keys present only in `curr` (added), only in `prev` (removed),
|
||||
// and present in both but whose values differ (changed). Each slice is sorted
|
||||
// lexicographically for stable logging output; value differences are determined
|
||||
// using deep equality.
|
||||
func diffPolicies(prev, curr *Policy) (added, removed, changed []string) {
|
||||
prevKeys := mapOf(prev)
|
||||
currKeys := mapOf(curr)
|
||||
@@ -127,7 +134,9 @@ func diffPolicies(prev, curr *Policy) (added, removed, changed []string) {
|
||||
|
||||
// mapOf returns a (possibly empty, never nil) copy of the underlying values
|
||||
// map of a Policy so callers outside this package can compare across the
|
||||
// public Policy boundary without touching unexported state.
|
||||
// mapOf returns a non-nil copy of the given Policy's key/value map.
|
||||
// If p is nil, mapOf returns an empty map; otherwise it returns a newly
|
||||
// allocated map containing the same key/value pairs as p.values.
|
||||
func mapOf(p *Policy) map[string]any {
|
||||
if p == nil {
|
||||
return map[string]any{}
|
||||
|
||||
Reference in New Issue
Block a user