model/api_token_unified_scope_test.go
Rama master · réplica de lectura del repositorio original.
package model
import (
"slices"
"testing"
)
// 这些测试约束「scope 命名统一」契约:
// - 只有 nezha:* 一套是 first-class scope;
// - mcp:* 不再作为 HasScope 的别名(避免 mcp:fs:write 静默扩到 REST 的 nezha:server:write);
// - AllScopes / AdminOnlyScopes 不再包含 mcp:*,新建 token 不能再签发它们。
//
// 旧 mcp:* 兼容由 createAPIToken 入口做一次性归一化(mcp:fs:read 等只读/exec 映射到
// 对应的 nezha:* read/exec),但 write/delete 类不再映射;详见 controller.createAPIToken。
func TestAllScopes_DoesNotExposeLegacyMCPScopes(t *testing.T) {
legacy := []string{
"mcp:*",
"mcp:server:read",
"mcp:server:exec",
"mcp:fs:read",
"mcp:fs:write",
"mcp:fs:delete",
}
for _, s := range legacy {
if slices.Contains(AllScopes, s) {
t.Errorf("AllScopes must not advertise legacy scope %q; only nezha:* is first-class", s)
}
if slices.Contains(AdminOnlyScopes, s) {
t.Errorf("AdminOnlyScopes must not advertise legacy scope %q", s)
}
}
}
func TestHasScope_LegacyMCPNoLongerAliasesNezhaWrite(t *testing.T) {
// 旧 token 数据库里残留 mcp:fs:write,绝不允许覆盖 REST 的 nezha:server:write。
tok := &APIToken{ScopesCSV: "mcp:fs:write"}
if tok.HasScope(ScopeServerWrite) {
t.Fatalf("legacy mcp:fs:write must NOT grant nezha:server:write via HasScope; " +
"REST routes (server/config, server/:id, batch-delete/server) would become reachable")
}
if tok.HasScope(ScopeServerDelete) {
t.Fatalf("legacy mcp:fs:write must NOT grant nezha:server:delete")
}
}
func TestHasScope_LegacyMCPDeleteNoLongerAliasesNezhaDelete(t *testing.T) {
tok := &APIToken{ScopesCSV: "mcp:fs:delete"}
if tok.HasScope(ScopeServerDelete) {
t.Fatalf("legacy mcp:fs:delete must NOT grant nezha:server:delete via HasScope")
}
}
func TestHasScope_LegacyMCPAllNoLongerWildcards(t *testing.T) {
tok := &APIToken{ScopesCSV: "mcp:*"}
for _, s := range []string{ScopeServerRead, ScopeServerWrite, ScopeServerDelete, ScopeServerExec} {
if tok.HasScope(s) {
t.Errorf("legacy mcp:* must not be treated as a nezha:* wildcard; granted %s", s)
}
}
}
func TestHasScope_NezhaWildcardStillWorks(t *testing.T) {
tok := &APIToken{ScopesCSV: ScopeNezhaAll}
for _, s := range []string{ScopeServerRead, ScopeServerWrite, ScopeServerDelete, ScopeServerExec} {
if !tok.HasScope(s) {
t.Errorf("nezha:* wildcard must still cover %s", s)
}
}
}
func TestHasScope_NezhaResourceWildcardStillWorks(t *testing.T) {
tok := &APIToken{ScopesCSV: "nezha:server:*"}
for _, s := range []string{ScopeServerRead, ScopeServerWrite, ScopeServerDelete, ScopeServerExec} {
if !tok.HasScope(s) {
t.Errorf("nezha:server:* must cover %s", s)
}
}
if tok.HasScope(ScopeServiceRead) {
t.Fatalf("nezha:server:* must NOT leak into nezha:service:* family")
}
}