package usecase import ( "context" "fmt" "log" "net/http" "time" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/permission" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/repository" "code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/usecase" "code.30cm.net/digimon/library-go/errs" "code.30cm.net/digimon/library-go/errs/code" "github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2/model" "github.com/zeromicro/go-zero/core/logx" ) type RBACUseCaseParam struct { ModulePath string permissionRepo repository.PermissionRepository roleRepo repository.RoleRepository rolePermission repository.RolePermissionRepository RBACRedisAdapter repository.RBACAdapter // role permission 之類的 } type RBACUseCase struct { permissionRepo repository.PermissionRepository roleRepo repository.RoleRepository adapter repository.RBACAdapter rolePermission repository.RolePermissionRepository instance *casbin.Enforcer } func NewRBACUseCase(param RBACUseCaseParam) usecase.RBACUseCase { result := &RBACUseCase{ adapter: param.RBACRedisAdapter, permissionRepo: param.permissionRepo, roleRepo: param.roleRepo, rolePermission: param.rolePermission, } // 1. 讀取 RBAC 模型 -> m, err := model.NewModelFromFile(param.ModulePath) if err != nil { log.Fatalf("failed to load model: %v", err) } // 3. 創建 Casbin Enforcer enforcer, err := casbin.NewEnforcer(m, result.adapter) if err != nil { log.Fatalf("failed to init Enforcer: %v", err) } result.instance = enforcer return result } func (use *RBACUseCase) Check(ctx context.Context, role, path, method string) (usecase.CheckRolePermissionStatus, error) { ok, p, err := use.instance.EnforceEx(role, path, method) if err != nil { e := errs.ForbiddenL(logx.WithContext(ctx), []logx.LogField{ {Key: "req", Value: fmt.Sprintf("role: %s, path: %s, method: %s", role, path, method)}, {Key: "func", Value: "casbin.EnforceEx"}, {Key: "err", Value: err.Error()}, }, "failed to get permission") return usecase.CheckRolePermissionStatus{}, e } status := usecase.CheckRolePermissionStatus{ Allow: ok, } // 檢查是否有明碼查詢權限 if role == permission.AdminRoleUID { status.Select.PlainCode = true } else if ok && method == http.MethodGet { policy, err := use.instance.GetModel().HasPolicy("p", "p", []string{ role, path, method, p[3] + ".plain_code", }) if err != nil { return usecase.CheckRolePermissionStatus{}, err } status.Select.PlainCode = policy } limit := 4 if len(p) >= limit { status.Select.PermissionName = p[3] } return status, nil } func (use *RBACUseCase) LoadPolicy(ctx context.Context) error { // 取得所有permission -> permission tree 拿到所有節點, permissions, err := use.permissionRepo.GetAll(ctx, nil) if err != nil { return fmt.Errorf("permissionRepo.AllStatus error: %w", err) } // 建立樹,我只要開啟的 Permission tree := GeneratePermissionTree(permissions) openMaps, err := tree.filterOpenNodes() if err != nil { return fmt.Errorf("GeneratePermissionTree.filterOpenNodes error: %w", err) } // 全部permission permissionMap := make(map[string]entity.Permission, len(permissions)) for k, v := range openMaps { permissionMap[k] = v } // 全部角色 roles, err := use.roleRepo.All(ctx, nil) if err != nil { return fmt.Errorf("roleRepo.All error: %w", err) } roleMap := make(map[string]entity.Role, len(roles)) for _, v := range roles { tmpValue := v roleMap[v.ID.Hex()] = *tmpValue } // 根據角色組合權限表 for _, role := range roles { rolePermission, err := use.rolePermission.Get(ctx, role.ID.Hex()) if err != nil { e := errs.DatabaseErrorWithScopeL( code.CloudEPPermission, domain.FailedToGetRolePermission, logx.WithContext(ctx), []logx.LogField{ {Key: "req", Value: fmt.Sprintf("role: %s", role.ID.Hex())}, {Key: "func", Value: "RolePermissionRepo.Get"}, {Key: "err", Value: err.Error()}, }, "failed to get rolePermission") return e } for _, rp := range rolePermission { r, ok := roleMap[rp.RoleID] if !ok { logx.Errorf(fmt.Sprintf("role_id: %s not found", rp.RoleID)) continue } p, ok := permissionMap[rp.PermissionID] if !ok { logx.Errorf(fmt.Sprintf("permission_id: %s not found", rp.PermissionID)) continue } if p.HTTPPath == "" || p.HTTPMethod == "" { continue } _, err = use.instance.AddPolicy(r.Name, p.HTTPPath, p.HTTPMethod, p.Name) if err != nil { return err } } } err = use.instance.LoadPolicy() if err != nil { return err } return nil } func (use *RBACUseCase) SyncPolicy(ctx context.Context, cron time.Duration) { t := time.NewTicker(cron) for { select { case <-t.C: if err := use.LoadPolicy(ctx); err == nil { logx.Info("LoadPolicy success") } case <-ctx.Done(): t.Stop() logx.Info("exit Policy success") return } } }