Skip to content

feat: add MDM managed app configuration support for Android and iOS - #5986

Closed
dbrieck wants to merge 4 commits into
netbirdio:mainfrom
dbrieck:feat/mdm-managed-config
Closed

dbrieck wants to merge 4 commits into
netbirdio:mainfrom
dbrieck:feat/mdm-managed-config

Conversation

@dbrieck

@dbrieck dbrieck commented Apr 24, 2026 •

Copy link
Copy Markdown

Summary

Add MDM (Mobile Device Management) managed app configuration support to the Go SDK, enabling Android and iOS native apps to receive and apply configuration from enterprise MDM solutions (Intune, Google Workspace, Jamf, etc.).

Fixes #1918

Changes

Go SDK - Android (client/android/)

  • managed_config.go — New ManagedConfig struct with gomobile-compatible setter API for 7 MDM keys: managementUrl, setupKey, adminUrl, preSharedKey, rosenpassEnabled, rosenpassPermissive, disableAutoConnect
  • managed_config_test.go — Unit tests covering setters, Apply(), config override, key constants
  • login.go — Added LoginWithSetupKeySync() for silent setup key registration from MDM

Go SDK - iOS (client/ios/NetBirdSDK/)

  • managed_config.go — iOS variant using DirectUpdateOrCreateConfig() for tvOS sandbox compatibility
  • login.go — Added LoginWithSetupKeySync() matching Android API

How It Works

  1. Native app reads MDM config (Android: RestrictionsManager, iOS: UserDefaults managed domain)
  2. Populates ManagedConfig via setter methods (gomobile-compatible)
  3. Calls Apply(configPath) to write values to NetBird client config
  4. Optionally calls LoginWithSetupKeySync() for zero-touch enrollment

MDM values are authoritative — they override any existing user configuration on every app launch.

Companion PRs

  • android-client: netbirdio/android-client — Android Enterprise integration (app_restrictions.xml, ManagedConfigReader, EngineRunner integration)
  • ios-client: netbirdio/ios-client — Apple Managed App Configuration integration (ManagedConfigReader.swift, NetworkExtensionAdapter/PacketTunnelProvider integration)

Summary by CodeRabbit

  • New Features

    • Added synchronous login entry points for Android and iOS.
    • Added managed configuration support for Android Enterprise and iOS AppConfig: management URL, setup key, admin URL, pre-shared key, Rosenpass flags, and disable-auto-connect, with Apply persistence behavior.
  • Tests

    • Added comprehensive tests validating managed-config state, persistence behavior, and non-persistence of setup keys.

Add ManagedConfig type to the Go SDK that allows native Android and iOS
apps to pass MDM-provided configuration values (management URL, setup
key, admin URL, pre-shared key, rosenpass settings, auto-connect) into
the NetBird client configuration.

- Add ManagedConfig struct with gomobile-compatible setter API
- Add Apply() to write MDM values to client config via profilemanager
- Add LoginWithSetupKeySync() for silent setup key registration
- Add key constant getters for native code to reference MDM keys
- Android variant uses UpdateOrCreateConfig()
- iOS variant uses DirectUpdateOrCreateConfig() for tvOS compatibility
- Include unit tests for all ManagedConfig functionality

Fixes netbirdio#1918
@coderabbitai

coderabbitai Bot commented Apr 24, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds synchronous setup-key login methods for Android and iOS and introduces ManagedConfig for both platforms to accept MDM/EMM-provided values and persist persistent fields via the profile manager (setup key treated as non-persistent).

Changes

Android Login

Layer / File(s) Summary
Android login sync entrypoint
client/android/login.go
Added LoginWithSetupKeySync(setupKey, deviceName) error that calls the existing internal login+save helper and returns its error.

Android Managed Config

Layer / File(s) Summary
ManagedConfig implementation & tests
client/android/managed_config.go, client/android/managed_config_test.go
New ManagedConfig type, exported key-name getters, setters for managed fields (management/admin URL, setup key, pre-shared key, Rosenpass flags, disable auto-connect), presence helpers, and Apply(configPath) that persists only persistent fields via profilemanager.UpdateOrCreateConfig. Tests cover persistence, non-persistence of setup key, empty-PSK handling, and constant getters.

iOS Login

Layer / File(s) Summary
iOS login sync entrypoint
client/ios/NetBirdSDK/login.go
Added LoginWithSetupKeySync(setupKey, deviceName) error that reuses the internal login+save flow and returns its error.

iOS Managed Config

Layer / File(s) Summary
ManagedConfig implementation & tests
client/ios/NetBirdSDK/managed_config.go, client/ios/NetBirdSDK/managed_config_test.go
New ManagedConfig type and exported key-name getters mirroring Android; setters/getters, HasConfig/HasSetupKey, and Apply(configPath) that persists provided persistent values via profilemanager.DirectUpdateOrCreateConfig (setup key non-persistent). Tests validate default state, setters, Apply persistence and overrides, PreSharedKey persistence, setupKey non-persistence, empty-PSK handling, and key constants.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant App as ManagedConfig
    participant Logger as Logger
    participant PM as profilemanager
    participant FS as FileSystem
    Note over App,Logger: Apply(configPath)
    App->>Logger: log "Applying managed config"
    App->>PM: build ConfigInput with only set persistent fields
    PM->>FS: read existing config (if present)
    PM->>FS: write/update config file
    FS-->>PM: ack / error
    PM-->>App: return success / error
    App->>Logger: log success or wrap error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • netbirdio/netbird#4944 — Modifies/refactors the login helper (loginWithSetupKeyAndSaveConfig) that the new synchronous login methods call.
  • netbirdio/netbird#5010 — Changes to authentication/login implementation that intersect with additions of synchronous login entry points.

Suggested reviewers

  • pappz
  • lixmal

Poem

"🐰 I hop through code at morning's light,
Managed configs tucked in, everything right.
Setup keys whispered, not stored away,
Android and iOS now greet the day.
Syncs and saves — a small coder's delight."

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides comprehensive technical detail but does not follow the repository's template structure; missing checklist items completion, documentation section, and explicit issue/PR URLs. Fill out the template sections: complete the checklist items (mark appropriate boxes), select a documentation option, and provide the required docs PR URL if applicable.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add MDM managed app configuration support for Android and iOS' accurately and specifically summarizes the main change: adding MDM managed app configuration support for both platforms.
Linked Issues check ✅ Passed The PR successfully implements the core objectives from issue #1918: it provides managed configuration mechanism for Android/iOS with MDM support (Intune, Google Workspace, Jamf), setup key support, and platform-compatible configuration channels.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing MDM managed configuration: new ManagedConfig types, Apply methods, LoginWithSetupKeySync methods, and corresponding tests for both Android and iOS.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
client/ios/NetBirdSDK/managed_config.go (1)

134-168: Minor: log.Infof without format verbs.

Several of these calls (log.Infof("MDM: setting management URL"), log.Infof("MDM: setting admin URL"), log.Infof("MDM: setting pre-shared key")) have no format arguments and should just be log.Info(...). Go vet / govet.printf will not catch this, but some linters (e.g., staticcheck SA1006 variants) will. Trivial.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/ios/NetBirdSDK/managed_config.go` around lines 134 - 168, In the
managed_config.go code that builds profilemanager.ConfigInput (the block
referencing variable m and the ConfigInput struct), replace the log.Infof calls
that have no format verbs—specifically log.Infof("MDM: setting management URL"),
log.Infof("MDM: setting admin URL"), and log.Infof("MDM: setting pre-shared
key")—with log.Info(...) calls; keep the other log.Infof usages that include
formatting (e.g., Rosenpass and disableAutoConn) unchanged so formatting verbs
match their arguments.
client/android/managed_config.go (1)

1-174: Significant duplication between Android and iOS managed_config.go.

The two files are ~95% identical — same struct, setters, HasConfig, key constants, and Apply body. The only real difference is UpdateOrCreateConfig vs DirectUpdateOrCreateConfig. Consider extracting the shared logic into an internal unexported package (e.g. client/internal/managedconfig) or a shared file with build tags, with the platform files only providing the persistence callback. This will prevent the two implementations from drifting over time (e.g. when a new MDM field is added and one side is missed).

Not a blocker — fine to defer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/android/managed_config.go` around lines 1 - 174, The Android
managed_config.go is nearly identical to the iOS version causing duplication;
refactor by extracting shared types and logic (ManagedConfig struct, setters,
HasConfig, Get* key getters, Apply logic minus persistence call) into an
internal package or shared file (e.g., client/internal/managedconfig) and keep
platform files minimal: each platform file should only call into the shared
package and provide its specific persistence function (Android uses
profilemanager.UpdateOrCreateConfig, iOS uses DirectUpdateOrCreateConfig) — move
constants and methods like NewManagedConfig, SetManagementURL, SetSetupKey,
SetAdminURL, SetPreSharedKey, SetRosenpassEnabled, SetRosenpassPermissive,
SetDisableAutoConnect, HasSetupKey, GetSetupKey, HasConfig, and the Apply
orchestration logic into the shared package, and replace the Apply persistence
call in android.ManagedConfig.Apply to invoke the platform-specific persistence
callback supplied by the shared package.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/android/managed_config_test.go`:
- Around line 137-157: The test TestManagedConfig_SetupKeyNotWrittenToConfig
currently reads back with profilemanager.ReadConfig which strips unknown JSON
fields, so change the assertion to read the raw file bytes (use os.ReadFile)
after calling m.Apply(cfgFile) and assert the literal setup key string
("secret-setup-key") is not present (use bytes.Contains and fail the test if it
is); keep the existing ManagementURL check via profilemanager.ReadConfig but add
the raw-file assertion to ensure Apply() doesn't write the SetupKey.

In `@client/android/managed_config.go`:
- Around line 82-85: The SetPreSharedKey method on ManagedConfig currently
stores a pointer even for an empty string which can overwrite an existing PSK in
Apply() via input.PreSharedKey; change SetPreSharedKey to treat empty-string as
"absent" by not setting m.preSharedKey (leave nil) when key == "" and only
assign a pointer when key is non-empty, and apply the same guard in the iOS
managed_config.go counterpart so Apply() only sees a non-nil PreSharedKey when a
real PSK was provided.
- Around line 113-132: HasConfig() currently returns true when only setupKey is
set, which causes ManagedConfig.Apply to call
profilemanager.UpdateOrCreateConfig and create a default config; change the
logic so Apply only proceeds when there is at least one field that will actually
be persisted. Concretely, update ManagedConfig.HasConfig or add a short guard in
ManagedConfig.Apply to ignore setupKey (i.e., treat setupKey as non-persistent)
so Apply returns early when the only set field is setupKey; ensure this same
change is applied to the iOS counterpart (client/ios/.../managed_config.go) so
profilemanager.UpdateOrCreateConfig / createNewConfig / config.apply are not
invoked for setup-key-only MDM payloads.

---

Nitpick comments:
In `@client/android/managed_config.go`:
- Around line 1-174: The Android managed_config.go is nearly identical to the
iOS version causing duplication; refactor by extracting shared types and logic
(ManagedConfig struct, setters, HasConfig, Get* key getters, Apply logic minus
persistence call) into an internal package or shared file (e.g.,
client/internal/managedconfig) and keep platform files minimal: each platform
file should only call into the shared package and provide its specific
persistence function (Android uses profilemanager.UpdateOrCreateConfig, iOS uses
DirectUpdateOrCreateConfig) — move constants and methods like NewManagedConfig,
SetManagementURL, SetSetupKey, SetAdminURL, SetPreSharedKey,
SetRosenpassEnabled, SetRosenpassPermissive, SetDisableAutoConnect, HasSetupKey,
GetSetupKey, HasConfig, and the Apply orchestration logic into the shared
package, and replace the Apply persistence call in android.ManagedConfig.Apply
to invoke the platform-specific persistence callback supplied by the shared
package.

In `@client/ios/NetBirdSDK/managed_config.go`:
- Around line 134-168: In the managed_config.go code that builds
profilemanager.ConfigInput (the block referencing variable m and the ConfigInput
struct), replace the log.Infof calls that have no format verbs—specifically
log.Infof("MDM: setting management URL"), log.Infof("MDM: setting admin URL"),
and log.Infof("MDM: setting pre-shared key")—with log.Info(...) calls; keep the
other log.Infof usages that include formatting (e.g., Rosenpass and
disableAutoConn) unchanged so formatting verbs match their arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d43f7d00-3514-459e-9b92-b78f93081c77

📥 Commits

Reviewing files that changed from the base of the PR and between d6f08e4 and 5d8c87a.

📒 Files selected for processing (5)
  • client/android/login.go
  • client/android/managed_config.go
  • client/android/managed_config_test.go
  • client/ios/NetBirdSDK/login.go
  • client/ios/NetBirdSDK/managed_config.go

Comment thread client/android/managed_config_test.go
Comment thread client/android/managed_config.go Outdated
Comment thread client/android/managed_config.go
@dbrieck

dbrieck commented Apr 24, 2026

Copy link
Copy Markdown
Author

Thank you, I'll review these automated code review comments.

- SetPreSharedKey: treat empty string as absent (no override)
- Apply: skip config file write when only setupKey is set (not persisted)
- Add hasPersistentConfig() to separate setup-key-only from persistent fields
- Fix log.Infof → log.Info for calls without format verbs
- Test: verify setup key absence via raw file bytes (os.ReadFile)
- Add tests for empty PSK and setup-key-only Apply scenarios
@CLAassistant

CLAassistant commented Apr 24, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 2 committers have signed the CLA.

❌ David Brieck
❌ dbrieck-pengate


David Brieck seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
client/android/managed_config.go (1)

116-137: Optional: derive HasConfig from hasPersistentConfig to avoid duplicated field lists.

The two predicates enumerate the same six persistent fields. If a new persistent field is ever added (e.g., another *bool override), both lists must be kept in sync or Apply() and external HasConfig() callers will diverge silently.

♻️ Proposed dedup
 // HasConfig returns true if any configuration value was set by MDM
 func (m *ManagedConfig) HasConfig() bool {
-	return m.managementURL != "" ||
-		m.setupKey != "" ||
-		m.adminURL != "" ||
-		m.preSharedKey != nil ||
-		m.rosenpassEnabled != nil ||
-		m.rosenpassPerm != nil ||
-		m.disableAutoConn != nil
+	return m.setupKey != "" || m.hasPersistentConfig()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/android/managed_config.go` around lines 116 - 137, HasConfig currently
duplicates the persistent-field checks present in hasPersistentConfig; change
ManagedConfig.HasConfig to simply return m.hasPersistentConfig() || m.setupKey
!= "" so the persistent-field list is maintained in one place (keep function
names ManagedConfig.HasConfig and ManagedConfig.hasPersistentConfig and the
setupKey field check to preserve the original behavior).
client/android/managed_config_test.go (1)

25-48: Optional: extend Apply coverage to the remaining persistent fields.

The table-driven test confirms setters flip HasConfig(), but the Apply(...) → ReadConfig(...) round-trip is only exercised for ManagementURL, PreSharedKey, and RosenpassEnabled (within TestManagedConfig_ApplyOverridesExisting). A regression that fails to wire, e.g., input.AdminURL, input.RosenpassPermissive, or input.DisableAutoConnect from ManagedConfig into ConfigInput in Apply() would not be caught today.

🧪 Sketch of a table-driven persistence test
func TestManagedConfig_ApplyPersistsAllFields(t *testing.T) {
	cfgFile := filepath.Join(t.TempDir(), "netbird.json")

	m := NewManagedConfig()
	m.SetManagementURL("https://mgmt.example.com:443")
	m.SetAdminURL("https://admin.example.com:443")
	m.SetPreSharedKey("psk")
	m.SetRosenpassEnabled(true)
	m.SetRosenpassPermissive(true)
	m.SetDisableAutoConnect(true)
	if err := m.Apply(cfgFile); err != nil {
		t.Fatalf("Apply failed: %v", err)
	}

	cfg, err := profilemanager.ReadConfig(cfgFile)
	if err != nil {
		t.Fatalf("ReadConfig failed: %v", err)
	}
	if cfg.AdminURL.String() != "https://admin.example.com:443" {
		t.Errorf("AdminURL = %q", cfg.AdminURL.String())
	}
	if !cfg.RosenpassPermissive {
		t.Error("RosenpassPermissive not persisted")
	}
	if !cfg.DisableAutoConnect {
		t.Error("DisableAutoConnect not persisted")
	}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/android/managed_config_test.go` around lines 25 - 48, The
Apply→ReadConfig round-trip test coverage is missing for AdminURL,
RosenpassPermissive, and DisableAutoConnect, so regressions in
ManagedConfig.Apply not copying those fields to ConfigInput can go unnoticed;
add/extend a table-driven test (e.g., TestManagedConfig_ApplyPersistsAllFields)
that sets m.SetAdminURL(...), m.SetRosenpassPermissive(true), and
m.SetDisableAutoConnect(true) on a NewManagedConfig(), calls m.Apply(cfgFile),
then uses profilemanager.ReadConfig(cfgFile) and asserts cfg.AdminURL,
cfg.RosenpassPermissive, and cfg.DisableAutoConnect match the values set to
ensure Apply() correctly persists these fields from ManagedConfig into
ConfigInput.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@client/android/managed_config_test.go`:
- Around line 25-48: The Apply→ReadConfig round-trip test coverage is missing
for AdminURL, RosenpassPermissive, and DisableAutoConnect, so regressions in
ManagedConfig.Apply not copying those fields to ConfigInput can go unnoticed;
add/extend a table-driven test (e.g., TestManagedConfig_ApplyPersistsAllFields)
that sets m.SetAdminURL(...), m.SetRosenpassPermissive(true), and
m.SetDisableAutoConnect(true) on a NewManagedConfig(), calls m.Apply(cfgFile),
then uses profilemanager.ReadConfig(cfgFile) and asserts cfg.AdminURL,
cfg.RosenpassPermissive, and cfg.DisableAutoConnect match the values set to
ensure Apply() correctly persists these fields from ManagedConfig into
ConfigInput.

In `@client/android/managed_config.go`:
- Around line 116-137: HasConfig currently duplicates the persistent-field
checks present in hasPersistentConfig; change ManagedConfig.HasConfig to simply
return m.hasPersistentConfig() || m.setupKey != "" so the persistent-field list
is maintained in one place (keep function names ManagedConfig.HasConfig and
ManagedConfig.hasPersistentConfig and the setupKey field check to preserve the
original behavior).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de4eee7c-4107-48ec-a253-15300f53a199

📥 Commits

Reviewing files that changed from the base of the PR and between 5d8c87a and f216e7a.

📒 Files selected for processing (3)
  • client/android/managed_config.go
  • client/android/managed_config_test.go
  • client/ios/NetBirdSDK/managed_config.go
✅ Files skipped from review due to trivial changes (1)
  • client/ios/NetBirdSDK/managed_config.go

Add GetManagementURL() to ManagedConfig so native apps can pass the
MDM-provided management URL to NewAuth for setup key registration,
instead of passing an empty string which defaults to api.netbird.io.
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
client/ios/NetBirdSDK/managed_config.go (1)

54-199: Optional: consider deduplicating the iOS/Android ManagedConfig implementations.

This file and client/android/managed_config.go are structurally identical — same key constants, same struct, same setters/getters, same HasConfig / hasPersistentConfig / Apply scaffolding. The only substantive difference is DirectUpdateOrCreateConfig vs UpdateOrCreateConfig at Line 192. Any future change to the managed-config schema (e.g., adding a new MDM key) has to be made in lockstep across both files, and the test file currently only covers the Android variant.

One approach: move the shared logic into a small internal package (e.g., client/internal/managedconfig) that takes the persistence function as a dependency, and keep the two platform files as thin gomobile-facing wrappers. Non-blocking; happy to leave as-is if you prefer to keep the platform SDK surfaces fully self-contained.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/ios/NetBirdSDK/managed_config.go` around lines 54 - 199, The
Android/iOS ManagedConfig implementations are duplicated; refactor by extracting
the shared ManagedConfig type and its logic (fields, NewManagedConfig,
Set*/Get*/HasConfig/hasPersistentConfig, Apply scaffolding) into a new internal
package (e.g., client/internal/managedconfig) that accepts the platform-specific
persistence function as a dependency; keep the iOS file's wrapper to construct
the shared ManagedConfig and call it with
profilemanager.DirectUpdateOrCreateConfig (and Android will call
UpdateOrCreateConfig), so only the persistence call differs between platform
files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@client/ios/NetBirdSDK/managed_config.go`:
- Around line 54-199: The Android/iOS ManagedConfig implementations are
duplicated; refactor by extracting the shared ManagedConfig type and its logic
(fields, NewManagedConfig, Set*/Get*/HasConfig/hasPersistentConfig, Apply
scaffolding) into a new internal package (e.g., client/internal/managedconfig)
that accepts the platform-specific persistence function as a dependency; keep
the iOS file's wrapper to construct the shared ManagedConfig and call it with
profilemanager.DirectUpdateOrCreateConfig (and Android will call
UpdateOrCreateConfig), so only the persistence call differs between platform
files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0cd11c02-20d3-492a-aa31-20129273c2e8

📥 Commits

Reviewing files that changed from the base of the PR and between f216e7a and b517419.

📒 Files selected for processing (2)
  • client/android/managed_config.go
  • client/ios/NetBirdSDK/managed_config.go

Mirrors the coverage in client/android/managed_config_test.go:
- HasConfig / HasSetupKey / GetSetupKey / GetManagementURL behaviour
- Each setter marks HasConfig as true
- Apply writes management URL, PSK, Rosenpass flags to disk
- Apply overrides an existing user-set value
- Setup key is never written to the config file (verified via raw bytes)
- Setup-key-only config does not create a config file
- Empty PSK is ignored
- All key constant getters return expected strings

Uses //go:build ios to match the package build constraint, consistent
with the existing preferences_test.go in the same package.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dbrieck

dbrieck commented Jun 4, 2026

Copy link
Copy Markdown
Author

Update pushed in dbrieck:feat/mdm-managed-config at 5b92c098f:

  • Added iOS managed-config tests for the SDK-facing ManagedConfig behavior, including empty config, setters, setup key, management URL getter, apply behavior, and invalid URL handling.

Validation note:

  • go test ./client/ios/NetBirdSDK on the host excludes all package files because the package is //go:build ios.
  • GOOS=ios go test ./client/ios/NetBirdSDK includes the package but requires iOS external/cgo linking in this local environment, so it cannot complete here without the mobile build toolchain setup.

@sonarqubecloud

sonarqubecloud Bot commented Jun 4, 2026

Copy link
Copy Markdown

@dbrieck

dbrieck commented Jun 4, 2026

Copy link
Copy Markdown
Author

Small validation clarification: I have not been able to test the Android managed-config path locally on a device. The end-to-end device validation was done on iOS through Intune/AppConfig with the companion ios-client PR.

The Android-related SDK behavior remains covered by the existing managed-config implementation/tests in this PR branch, but reviewers should treat Android device validation as still pending.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
client/ios/NetBirdSDK/managed_config_test.go (1)

101-149: ⚡ Quick win

Expand Apply persistence coverage for the remaining managed keys.

The Apply tests validate persistence for ManagementURL, RosenpassEnabled, and PreSharedKey, but they don’t assert persisted behavior for AdminURL, RosenpassPermissive, and DisableAutoConnect. Please add one table-driven Apply test that writes each key and verifies it round-trips from netbird.json (including override semantics where relevant).

Also applies to: 205-217

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/ios/NetBirdSDK/managed_config_test.go` around lines 101 - 149, Add a
table-driven test that uses NewManagedConfig, calls SetAdminURL,
SetRosenpassPermissive, and SetDisableAutoConnect (and reuses
SetManagementURL/SetRosenpassEnabled/SetPreSharedKey where helpful), calls
m.Apply(cfgFile), then reads back the file with profilemanager.ReadConfig and
asserts cfg.AdminURL, cfg.RosenpassPermissive, and cfg.DisableAutoConnect
round-trip correctly (and include a case for override semantics e.g.,
ManagementURL present in Preferences then overridden by Apply). Structure as a
single TestManagedConfig_Apply_Table or extend existing tests to loop over cases
(name, setter func, expected field accessor) to cover AdminURL,
RosenpassPermissive, DisableAutoConnect and ensure each case writes, applies,
reads and asserts equality.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@client/ios/NetBirdSDK/managed_config_test.go`:
- Around line 101-149: Add a table-driven test that uses NewManagedConfig, calls
SetAdminURL, SetRosenpassPermissive, and SetDisableAutoConnect (and reuses
SetManagementURL/SetRosenpassEnabled/SetPreSharedKey where helpful), calls
m.Apply(cfgFile), then reads back the file with profilemanager.ReadConfig and
asserts cfg.AdminURL, cfg.RosenpassPermissive, and cfg.DisableAutoConnect
round-trip correctly (and include a case for override semantics e.g.,
ManagementURL present in Preferences then overridden by Apply). Structure as a
single TestManagedConfig_Apply_Table or extend existing tests to loop over cases
(name, setter func, expected field accessor) to cover AdminURL,
RosenpassPermissive, DisableAutoConnect and ensure each case writes, applies,
reads and asserts equality.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db2b5b9b-21b5-43fa-abc0-708233e80044

📥 Commits

Reviewing files that changed from the base of the PR and between b517419 and 5b92c09.

📒 Files selected for processing (1)
  • client/ios/NetBirdSDK/managed_config_test.go

@jumbi77

jumbi77 commented Sep 14, 2026

Copy link
Copy Markdown

Hey, is here an update on this? What is left to get this ingegrated?

@dbrieck

dbrieck commented Sep 14, 2026

Copy link
Copy Markdown
Author

Hey, is here an update on this? What is left to get this ingegrated?

I don't have my Mac with me today, but I'll sync and push to the PR after re-implementing it based on what the maintainers have done recently with the overlay. I'll have to test it tomorrow, but it'll at least get this close to actually being accepted.

@dbrieck

dbrieck commented Sep 14, 2026

Copy link
Copy Markdown
Author

Closing this because the core SDK portion has been superseded by #6435, which is now merged into main. The current implementation uses the PolicyFetcher-based MDM architecture, applies managed settings as policy overlays rather than persisting them into the base configuration, and provides the mobile APIs used by the platform integrations.

Development of the native integrations continues in:

Thank you to everyone who reviewed and helped shape this work.

@dbrieck dbrieck closed this Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android/iOS managed configurations

4 participants