Files
backend/internal/service/casbin_service_test.go

187 lines
6.5 KiB
Go
Raw Normal View History

package service
import (
"context"
"testing"
withmodel "with_you/internal/model"
"github.com/casbin/casbin/v3"
casbinmodel "github.com/casbin/casbin/v3/model"
)
// newTestEnforcer 构造一个内存 Enforcer加载与生产相同的 model.conf 语义globMatch
// 并按 seedPermissions 的约定注入角色策略,便于在不依赖 DB 的情况下验证 matcher 行为。
func newTestEnforcer(t *testing.T) *casbin.Enforcer {
t.Helper()
// 与 configs/casbin/model.conf 保持一致;用 globMatch 而非 keyMatch2。
mText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && globMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
`
m, err := casbinmodel.NewModelFromString(mText)
if err != nil {
t.Fatalf("parse model: %v", err)
}
e, err := casbin.NewEnforcer(m)
if err != nil {
t.Fatalf("new enforcer: %v", err)
}
// 与 seedPermissions 一致super_admin=admin.** 通配admin 业务能力,
// moderator 内容审核。admin.logs.export 仅 super_admin 拥有admin 只有 admin.logs read
policies := [][]string{
{withmodel.RoleSuperAdmin, "admin/**", "*"},
{withmodel.RoleAdmin, "admin/users", "read"},
{withmodel.RoleAdmin, "admin/users/status", "write"},
{withmodel.RoleAdmin, "admin/users/devices", "read"},
{withmodel.RoleAdmin, "admin/posts", "*"},
{withmodel.RoleAdmin, "admin/comments", "*"},
{withmodel.RoleAdmin, "admin/groups", "*"},
{withmodel.RoleAdmin, "admin/channels", "*"},
{withmodel.RoleAdmin, "admin/dashboard", "read"},
{withmodel.RoleAdmin, "admin/reports", "*"},
{withmodel.RoleAdmin, "admin/verifications", "*"},
{withmodel.RoleAdmin, "admin/profile_audits", "*"},
{withmodel.RoleAdmin, "admin/materials", "*"},
{withmodel.RoleAdmin, "admin/logs", "read"},
{withmodel.RoleModerator, "admin/posts", "read"},
{withmodel.RoleModerator, "admin/posts", "write"},
{withmodel.RoleModerator, "admin/comments", "read"},
{withmodel.RoleModerator, "admin/comments", "write"},
{withmodel.RoleModerator, "admin/reports", "read"},
{withmodel.RoleModerator, "admin/reports", "write"},
}
for _, p := range policies {
if _, err := e.AddPolicy(p[0], p[1], p[2]); err != nil {
t.Fatalf("add policy %v: %v", p, err)
}
}
return e
}
// mustAllow / mustDeny 简化断言。
func mustAllow(t *testing.T, e *casbin.Enforcer, sub, obj, act string) {
t.Helper()
ok, err := e.Enforce(sub, obj, act)
if err != nil {
t.Fatalf("Enforce(%s,%s,%s) err: %v", sub, obj, act, err)
}
if !ok {
t.Errorf("Enforce(%s,%s,%s) = false, want true", sub, obj, act)
}
}
func mustDeny(t *testing.T, e *casbin.Enforcer, sub, obj, act string) {
t.Helper()
ok, err := e.Enforce(sub, obj, act)
if err != nil {
t.Fatalf("Enforce(%s,%s,%s) err: %v", sub, obj, act, err)
}
if ok {
t.Errorf("Enforce(%s,%s,%s) = true, want false", sub, obj, act)
}
}
// TestSuperAdminWildcard 通配多级资源globMatch ** 跨 '.')。
func TestSuperAdminWildcard(t *testing.T) {
e := newTestEnforcer(t)
mustAllow(t, e, withmodel.RoleSuperAdmin, "admin/users/roles/write", "write")
mustAllow(t, e, withmodel.RoleSuperAdmin, "admin/logs/export", "read")
mustAllow(t, e, withmodel.RoleSuperAdmin, "admin/dashboard", "read")
}
// TestAdminBusinessCapabilities admin 拥有的业务能力放行。
func TestAdminBusinessCapabilities(t *testing.T) {
e := newTestEnforcer(t)
mustAllow(t, e, withmodel.RoleAdmin, "admin/posts", "write")
mustAllow(t, e, withmodel.RoleAdmin, "admin/posts", "read")
mustAllow(t, e, withmodel.RoleAdmin, "admin/users", "read")
mustAllow(t, e, withmodel.RoleAdmin, "admin/logs", "read")
}
// TestAdminCannotReachSuperAdminOnlyResources admin 不能触及 super_admin 独占资源。
// - admin.logs.export 仅 super_admin 拥有admin 应被拒。
// - admin.users.roles.write 仅 super_admin 拥有。
// - admin.roles.* 仅 super_admin 拥有。
func TestAdminCannotReachSuperAdminOnlyResources(t *testing.T) {
e := newTestEnforcer(t)
mustDeny(t, e, withmodel.RoleAdmin, "admin/logs/export", "read")
mustDeny(t, e, withmodel.RoleAdmin, "admin/users/roles", "write")
mustDeny(t, e, withmodel.RoleAdmin, "admin/roles", "read")
// admin.users.status.write 是 admin 自己的能力,确认不受影响。
mustAllow(t, e, withmodel.RoleAdmin, "admin/users/status", "write")
}
// TestModeratorContentModeration moderator 仅内容审核。
func TestModeratorContentModeration(t *testing.T) {
e := newTestEnforcer(t)
mustAllow(t, e, withmodel.RoleModerator, "admin/posts", "read")
mustAllow(t, e, withmodel.RoleModerator, "admin/comments", "write")
mustAllow(t, e, withmodel.RoleModerator, "admin/reports", "write")
// moderator 不应触及用户管理。
mustDeny(t, e, withmodel.RoleModerator, "admin/users", "read")
mustDeny(t, e, withmodel.RoleModerator, "admin/logs", "read")
}
// TestWildcardDoesNotCrossBoundaryWithSingleStar 校验 admin/* 在 globMatch 下不跨 '/'。
// 此用例用于防止未来有人误把 super_admin 通配写回 admin/*(单层),导致 super_admin 实际拿不到
// admin/users/roles/write 这类多级资源的能力(应使用 admin/**)。
//
// 注意:本测试构造独立的 Enforcer仅注入 admin/* 单层策略,避免 newTestEnforcer 的
// admin/** 策略干扰断言。
func TestWildcardDoesNotCrossBoundaryWithSingleStar(t *testing.T) {
mText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && globMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
`
m, err := casbinmodel.NewModelFromString(mText)
if err != nil {
t.Fatalf("parse model: %v", err)
}
e, err := casbin.NewEnforcer(m)
if err != nil {
t.Fatalf("new enforcer: %v", err)
}
if _, err := e.AddPolicy(withmodel.RoleSuperAdmin, "admin/*", "*"); err != nil {
t.Fatalf("add policy: %v", err)
}
// 单层 * 不跨 '/'admin/* 仅命中 admin/<segment>,不能命中 admin/users/roles/write。
mustDeny(t, e, withmodel.RoleSuperAdmin, "admin/users/roles/write", "write")
mustAllow(t, e, withmodel.RoleSuperAdmin, "admin/posts", "write")
}
// TestEnforceForUser_NoRolesNoPanic 用户无角色时 EnforceForUser 走默认 user 角色。
func TestEnforceForUser_NoRolesNoPanic(t *testing.T) {
e := newTestEnforcer(t)
// user 角色无任何 admin 策略,应全部拒绝。
mustDeny(t, e, "user", "admin/posts", "read")
// 防止 lint 抱怨未使用 context。
_ = context.Background()
}