Files
netbird/client/mdm/conflicts_test.go
Riccardo Manfrin d2e62e358a [client] Compare MDM-managed URLs as endpoints, not as strings (#7472)
A policy that enforces a management URL refuses any SetConfig or Login whose
URL differs from it. The comparison normalized only the default port, so
three ways of writing the very endpoint the policy names were reported as
conflicts:

  policy https://mgmt.example.com  vs  https://mgmt.example.com/     refused
                                       https://MGMT.example.com      refused
                                       https://mgmt.example.com:0443 refused

For an MDM-managed deployment whose stored or command-line URL is spelled
differently from the policy's value, that means every settings update is
refused with an MDMManagedFieldsViolation naming a field the caller did not
change. `netbird up --management-url https://MGMT.example.com` reproduces it.

The rules now live in util.SameServiceURL, and ConflictURL delegates: scheme
and host compared case-insensitively, the effective port normalized
numerically, a trailing slash ignored, and a path otherwise still part of the
identity so /other remains a divergence. Unparseable input falls back to
string equality.

util rather than either caller, because comparing two service URLs is
neither device management nor profile storage, and more than one place does
it: an MDM-enforced management URL against a requested one here, a stored
profile URL against a command-line one in profilemanager and the SSH gate.
Every copy of these rules that drifts turns an equivalent URL into a refused
request, which is how this one arose.

CanonicalURL is left alone: besides comparison it is the canonical value
handed to mdm.Restrictions and to the Android and iOS Preferences getters,
and normalizing what those return is a separate decision.
2026-09-08 16:32:16 +02:00

41 lines
1.4 KiB
Go

package mdm
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// The same spellings, through the conflict check that decides whether a request
// is refused. An enforced URL restated in another spelling addresses the very
// server the policy names, so it must not be reported as a conflict.
func TestConflictURLComparesEndpoints(t *testing.T) {
policy := NewPolicy(map[string]any{KeyManagementURL: "https://mgmt.example.com"})
require.True(t, policy.HasKey(KeyManagementURL))
for _, restated := range []string{
"https://mgmt.example.com",
"https://mgmt.example.com:443",
"https://mgmt.example.com/",
"https://MGMT.example.com",
"https://mgmt.example.com:0443",
} {
conflicts := ResolveConflicts(policy, []ConflictCheck{ConflictURL(KeyManagementURL, restated)})
assert.Empty(t, conflicts, "%q is the enforced endpoint written differently", restated)
}
for _, diverging := range []string{
"https://other.example.com",
"http://mgmt.example.com",
"https://mgmt.example.com:8443",
"https://mgmt.example.com/other",
} {
conflicts := ResolveConflicts(policy, []ConflictCheck{ConflictURL(KeyManagementURL, diverging)})
assert.Equal(t, []string{KeyManagementURL}, conflicts, "%q addresses another endpoint", diverging)
}
// An unset field is not a request to change anything.
assert.Empty(t, ResolveConflicts(policy, []ConflictCheck{ConflictURL(KeyManagementURL, "")}))
}