package authz_test import ( "context" "testing" "chat-agent-system/internal/authz" ) func TestService_ListCapabilities_DisabledWithoutPool(t *testing.T) { svc := authz.NewService(nil) caps, err := svc.ListCapabilities(context.Background(), 1) if err != nil { t.Fatalf("unexpected error: %v", err) } if caps != nil { t.Fatalf("expected nil caps when pool disabled, got %v", caps) } } func TestService_IsSystemAdmin_ByRoleID(t *testing.T) { svc := authz.NewService(nil) admin, err := svc.IsSystemAdmin(context.Background(), 1, "role-admin") if err != nil || !admin { t.Fatalf("expected admin by role_id, got (%v, %v)", admin, err) } } func TestService_HasSystemPermission_AdminGrantsAllSystemKeys(t *testing.T) { svc := authz.NewService(nil) ctx := context.Background() for _, perm := range authz.SystemConsolePermissions() { ok, err := svc.HasSystemPermission(ctx, 1, "role-admin", perm) if err != nil || !ok { t.Fatalf("admin should have %s, got (%v, %v)", perm, ok, err) } } ok, err := svc.HasSystemPermission(ctx, 1, "role-admin", authz.PermManage) if err != nil || !ok { t.Fatalf("admin should have perm:manage, got (%v, %v)", ok, err) } } func TestService_HasSystemPermission_NonAdminFailClosed(t *testing.T) { svc := authz.NewService(nil) ctx := context.Background() for _, role := range []string{"role-engineer", "role-guest", "", "role-director"} { for _, perm := range authz.SystemConsolePermissions() { ok, err := svc.HasSystemPermission(ctx, 2, role, perm) if err != nil { t.Fatalf("unexpected err for %s/%s: %v", role, perm, err) } if ok { t.Fatalf("non-admin %s must not have %s", role, perm) } } } ok, err := svc.HasSystemPermission(ctx, 0, "role-admin", authz.PermSystemOverview) if err != nil || ok { t.Fatalf("invalid userID must fail-closed, got (%v, %v)", ok, err) } ok, err = svc.HasSystemPermission(ctx, 1, "role-admin", "") if err != nil || ok { t.Fatalf("empty perm must fail-closed, got (%v, %v)", ok, err) } } func TestPermissionCatalog_IncludesSystemConsoleKeys(t *testing.T) { keys := map[string]bool{} for _, e := range authz.PermissionCatalog() { keys[e.Key] = true } for _, perm := range authz.SystemConsolePermissions() { if !keys[perm] { t.Fatalf("catalog missing %s", perm) } } } func TestUpsertProjectMember_RejectsDocRolesWithoutDB(t *testing.T) { a := authz.NewAuthorizer(nil) if err := a.UpsertProjectMember(context.Background(), 1, 2, "author"); err == nil { t.Fatal("expected author role to be rejected") } if err := a.UpsertProjectMember(context.Background(), 1, 2, authz.RoleOwner); err != nil { t.Fatalf("owner should be allowed: %v", err) } bad := "viewer" if err := a.UpsertProjectMemberSpec(context.Background(), 1, 2, authz.MemberUpsert{ ProjectRole: authz.RoleMember, DocumentDuty: &bad, }); err == nil { t.Fatal("document_duty=viewer must be rejected") } } func TestGetProjectMember_DisabledWithoutPool(t *testing.T) { a := authz.NewAuthorizer(nil) rec, err := a.GetProjectMember(context.Background(), 1, 2) if err != nil || rec != nil { t.Fatalf("disabled authorizer must not invent a member, got (%v, %v)", rec, err) } } // TestMembershipChecks_FailClosedWithoutPool FIND-SEC-02 回归: // 无池 Authorizer 的成员/owner 判定必须 fail-closed,不得放行任意登录用户。 func TestMembershipChecks_FailClosedWithoutPool(t *testing.T) { a := authz.NewAuthorizer(nil) for name, fn := range map[string]func() (bool, error){ "IsProjectMember": func() (bool, error) { return a.IsProjectMember(context.Background(), 1, 2) }, "IsProjectOwner": func() (bool, error) { return a.IsProjectOwner(context.Background(), 1, 2) }, } { ok, err := fn() if err != nil || ok { t.Fatalf("%s without pool must fail closed (false, nil), got (%v, %v)", name, ok, err) } } if err := a.RequireProjectAccess(context.Background(), 1, 2); err == nil { t.Fatalf("RequireProjectAccess without pool must deny, got nil error") } }