Files
backend/internal/service/casbin_service.go
lan d3065b30cc feat(admin): add comprehensive admin management system
- Add admin handlers and services for users, posts, comments, groups, and dashboard
- Implement role permission management with ability to view and update permissions
- Add database seeding for roles and Casbin permission policies
- Upgrade Casbin from v2 to v3 with GORM adapter for persistent policy storage
- Add admin-specific repository methods with filtering and pagination
- Register new admin API routes under /api/v1/admin/*
2026-03-14 18:01:55 +08:00

266 lines
6.6 KiB
Go

package service
import (
"context"
"fmt"
"time"
"carrot_bbs/internal/cache"
"carrot_bbs/internal/config"
"carrot_bbs/internal/repository"
"github.com/casbin/casbin/v3"
)
// CasbinService Casbin 权限服务接口
type CasbinService interface {
// Enforce 检查权限
Enforce(ctx context.Context, sub, obj, act string) (bool, error)
// EnforceForUser 检查用户权限 (自动获取用户角色)
EnforceForUser(ctx context.Context, userID, obj, act string) (bool, error)
// AddRoleForUser 为用户添加角色
AddRoleForUser(ctx context.Context, userID, role string) error
// DeleteRoleForUser 移除用户角色
DeleteRoleForUser(ctx context.Context, userID, role string) error
// GetRolesForUser 获取用户所有角色
GetRolesForUser(ctx context.Context, userID string) ([]string, error)
// HasRoleForUser 检查用户是否拥有指定角色
HasRoleForUser(ctx context.Context, userID, role string) (bool, error)
// AddPolicy 添加策略
AddPolicy(ctx context.Context, role, resource, action string) error
// RemovePolicy 移除策略
RemovePolicy(ctx context.Context, role, resource, action string) error
// LoadPolicy 从数据库重新加载策略
LoadPolicy(ctx context.Context) error
// ClearCache 清除权限缓存
ClearCache(ctx context.Context) error
// GetPermissionsForRole 获取角色的所有权限
GetPermissionsForRole(ctx context.Context, role string) ([][]string, error)
// UpdatePermissionsForRole 更新角色的权限(先删除所有权限,再添加新权限)
UpdatePermissionsForRole(ctx context.Context, role string, permissions [][]string) error
// GetAllPermissions 获取系统中所有权限定义
GetAllPermissions(ctx context.Context) [][]string
}
type casbinService struct {
enforcer *casbin.Enforcer
cache cache.Cache
cacheTTL time.Duration
roleRepo repository.RoleRepository
skipRoutes map[string]bool
}
func NewCasbinService(
enforcer *casbin.Enforcer,
cache cache.Cache,
roleRepo repository.RoleRepository,
cfg *config.CasbinConfig,
) CasbinService {
skipRoutes := make(map[string]bool)
for _, route := range cfg.SkipRoutes {
skipRoutes[route] = true
}
return &casbinService{
enforcer: enforcer,
cache: cache,
cacheTTL: time.Duration(cfg.CacheTTL) * time.Second,
roleRepo: roleRepo,
skipRoutes: skipRoutes,
}
}
func (s *casbinService) Enforce(ctx context.Context, sub, obj, act string) (bool, error) {
// 检查是否跳过权限检查
if s.skipRoutes[obj] {
return true, nil
}
// 检查缓存
if s.cache != nil {
cacheKey := fmt.Sprintf("casbin:check:%s:%s:%s", sub, obj, act)
if cached, ok := s.cache.Get(cacheKey); ok {
if allowed, ok := cached.(bool); ok {
return allowed, nil
}
}
}
// 执行权限检查
allowed, err := s.enforcer.Enforce(sub, obj, act)
if err != nil {
return false, err
}
// 缓存结果
if s.cache != nil {
cacheKey := fmt.Sprintf("casbin:check:%s:%s:%s", sub, obj, act)
s.cache.Set(cacheKey, allowed, s.cacheTTL)
}
return allowed, nil
}
func (s *casbinService) EnforceForUser(ctx context.Context, userID, obj, act string) (bool, error) {
// 获取用户角色
roles, err := s.GetRolesForUser(ctx, userID)
if err != nil {
return false, err
}
// 如果用户没有角色,使用默认角色
if len(roles) == 0 {
roles = []string{"user"} // 默认角色
}
// 检查每个角色的权限
for _, role := range roles {
allowed, err := s.Enforce(ctx, role, obj, act)
if err != nil {
return false, err
}
if allowed {
return true, nil
}
}
return false, nil
}
func (s *casbinService) AddRoleForUser(ctx context.Context, userID, role string) error {
// 添加到数据库
if err := s.roleRepo.AddRoleForUser(ctx, userID, role); err != nil {
return err
}
// 添加到 Casbin
if _, err := s.enforcer.AddRoleForUser(userID, role); err != nil {
return err
}
// 清除缓存
return s.clearUserCache(ctx, userID)
}
func (s *casbinService) DeleteRoleForUser(ctx context.Context, userID, role string) error {
// 从数据库删除
if err := s.roleRepo.DeleteRoleForUser(ctx, userID, role); err != nil {
return err
}
// 从 Casbin 删除
if _, err := s.enforcer.DeleteRoleForUser(userID, role); err != nil {
return err
}
// 清除缓存
return s.clearUserCache(ctx, userID)
}
func (s *casbinService) GetRolesForUser(ctx context.Context, userID string) ([]string, error) {
// 检查缓存
if s.cache != nil {
cacheKey := fmt.Sprintf("casbin:user:roles:%s", userID)
if cached, ok := s.cache.Get(cacheKey); ok {
if roles, ok := cached.([]string); ok {
return roles, nil
}
}
}
// 从数据库获取
roles, err := s.roleRepo.GetUserRoles(ctx, userID)
if err != nil {
return nil, err
}
// 缓存结果
if s.cache != nil {
cacheKey := fmt.Sprintf("casbin:user:roles:%s", userID)
s.cache.Set(cacheKey, roles, s.cacheTTL)
}
return roles, nil
}
func (s *casbinService) HasRoleForUser(ctx context.Context, userID, role string) (bool, error) {
return s.roleRepo.HasRole(ctx, userID, role)
}
func (s *casbinService) AddPolicy(ctx context.Context, role, resource, action string) error {
if _, err := s.enforcer.AddPolicy(role, resource, action); err != nil {
return err
}
return s.ClearCache(ctx)
}
func (s *casbinService) RemovePolicy(ctx context.Context, role, resource, action string) error {
if _, err := s.enforcer.RemovePolicy(role, resource, action); err != nil {
return err
}
return s.ClearCache(ctx)
}
func (s *casbinService) LoadPolicy(ctx context.Context) error {
return s.enforcer.LoadPolicy()
}
func (s *casbinService) ClearCache(ctx context.Context) error {
if s.cache != nil {
// 清除所有 casbin 相关缓存
s.cache.DeleteByPrefix("casbin:")
}
return nil
}
func (s *casbinService) clearUserCache(ctx context.Context, userID string) error {
if s.cache != nil {
cacheKey := fmt.Sprintf("casbin:user:roles:%s", userID)
s.cache.Delete(cacheKey)
}
return nil
}
func (s *casbinService) GetPermissionsForRole(ctx context.Context, role string) ([][]string, error) {
return s.enforcer.GetPermissionsForUser(role)
}
func (s *casbinService) UpdatePermissionsForRole(ctx context.Context, role string, permissions [][]string) error {
// 先删除该角色的所有权限
_, err := s.enforcer.DeletePermissionsForUser(role)
if err != nil {
return err
}
// 添加新权限
for _, perm := range permissions {
if len(perm) >= 2 {
resource := perm[0]
action := perm[1]
if _, err := s.enforcer.AddPolicy(role, resource, action); err != nil {
return err
}
}
}
// 清除缓存
return s.ClearCache(ctx)
}
func (s *casbinService) GetAllPermissions(ctx context.Context) [][]string {
permissions, _ := s.enforcer.GetPolicy()
return permissions
}