fix(auth): add node-admin token scope for panel updates (#6666)

* feat(auth): add node-admin API token scope

Keep ordinary node-sync credentials unable to self-update a remote panel while allowing explicitly elevated node administrators to inherit synchronization access and invoke updatePanel.

* style(docs): format API scope table

* fix(auth): preserve mTLS node-sync semantics

Allow only an explicit node-admin bearer to elevate a verified mTLS request. Existing admin, monitor, and node-sync bearer values continue to use the historical node-sync identity on mTLS connections.

* chore(ci): rerun flaky checks

---------

Co-authored-by: libmur-dev <333915961+libmur-dev@users.noreply.github.com>
This commit is contained in:
libmur-dev
2026-10-18 00:45:26 +03:00
committed by GitHub
parent a1f2589eb5
commit a8d0700bf2
42 changed files with 506 additions and 123 deletions
@@ -29,7 +29,11 @@ func TestNodeSyncPushSkipsOperatorTLSGuard(t *testing.T) {
runtime.SetManager(runtime.NewManager(runtime.LocalDeps{APIPort: func() int { return 0 }, SetNeedRestart: func() {}}))
t.Cleanup(func() { runtime.SetManager(prev) })
for name, scope := range map[string]string{"node-sync": model.ApiScopeNodeSync, "admin": model.ApiScopeAdmin} {
for name, scope := range map[string]string{
"node-sync": model.ApiScopeNodeSync,
"node-admin": model.ApiScopeNodeAdmin,
"admin": model.ApiScopeAdmin,
} {
row := &model.ApiToken{Name: name, Token: crypto.HashTokenSHA256(name + "-token"), Enabled: true, Scope: scope}
if err := database.GetDB().Create(row).Error; err != nil {
t.Fatalf("seed %s token: %v", name, err)
@@ -79,6 +83,16 @@ func TestNodeSyncPushSkipsOperatorTLSGuard(t *testing.T) {
}
})
t.Run("a node admin push keeps node sync semantics", func(t *testing.T) {
body := add(t, "node-admin-token", 45003)
if !strings.Contains(body, `"success":true`) {
t.Fatalf("node-admin add rejected: %s", body)
}
if got := rows(t, "tls-legacy-45003"); got != 1 {
t.Fatalf("stored rows = %d, want 1", got)
}
})
t.Run("an operator token is still held to the guard", func(t *testing.T) {
body := add(t, "admin-token", 45002)
if !strings.Contains(body, `"success":false`) || !strings.Contains(body, "TLS") {