// // Copyright (C) 2024 veypi // 2025-03-04 16:08:06 // Distributed under terms of the MIT license. // package middleware import ( "strings" "github.com/veypi/vbase/cfg" "github.com/veypi/vbase/models" "github.com/veypi/vigo" ) // PolicyEvaluator 策略评估器 type PolicyEvaluator struct { checker *Checker } // NewPolicyEvaluator 创建策略评估器 func (c *Checker) NewPolicyEvaluator() *PolicyEvaluator { return &PolicyEvaluator{checker: c} } // PermissionCheck 权限检查参数 type PermissionCheck struct { Resource string // 资源: user, org, role, policy, project, etc. Action string // 操作: create, read, update, delete, list, etc. OrgID string // 组织ID(可选) OwnerID string // 资源所有者ID(用于条件判断) } // HasPermission 检查是否有权限 func (pe *PolicyEvaluator) HasPermission(check PermissionCheck) bool { if pe.checker.userID == "" { return false } // 平台超级管理员拥有所有权限 if pe.isPlatformAdmin() { return true } // 获取用户所有策略 policies := pe.getUserPolicies(check.OrgID) // 按优先级排序:deny > allow // 先检查 deny 策略 for _, policy := range policies { if policy.Effect != models.PolicyEffectDeny { continue } if pe.matchPolicy(policy, check) { return false } } // 再检查 allow 策略 for _, policy := range policies { if policy.Effect != models.PolicyEffectAllow { continue } if pe.matchPolicy(policy, check) { return true } } return false } // isPlatformAdmin 检查是否为平台管理员 func (pe *PolicyEvaluator) isPlatformAdmin() bool { return pe.checker.IsOrgOwner() } // getUserPolicies 获取用户拥有的所有策略 func (pe *PolicyEvaluator) getUserPolicies(orgID string) []models.Policy { var policies []models.Policy roles := pe.checker.GetUserRoles() if len(roles) == 0 { return policies } for _, roleID := range roles { var role models.Role if err := cfg.DB().First(&role, "id = ?", roleID).Error; err != nil { continue } if role.PolicyIDs == "" { continue } policyIDs := strings.Split(role.PolicyIDs, ",") var rolePolicies []models.Policy if err := cfg.DB().Where("id IN ?", policyIDs).Find(&rolePolicies).Error; err != nil { continue } policies = append(policies, rolePolicies...) } return policies } // matchPolicy 匹配策略 func (pe *PolicyEvaluator) matchPolicy(policy models.Policy, check PermissionCheck) bool { if !matchPattern(policy.Resource, check.Resource) { return false } if !matchPattern(policy.Action, check.Action) { return false } if policy.Condition != "" { if !pe.checkCondition(policy.Condition, check) { return false } } return true } // matchPattern 模式匹配(支持通配符 *) func matchPattern(pattern, value string) bool { if pattern == "*" { return true } if pattern == value { return true } if strings.HasSuffix(pattern, ":*") { prefix := strings.TrimSuffix(pattern, ":*") return strings.HasPrefix(value, prefix+":") } return false } // checkCondition 检查条件 func (pe *PolicyEvaluator) checkCondition(condition string, check PermissionCheck) bool { conditions := strings.Split(condition, ",") for _, c := range conditions { c = strings.TrimSpace(c) switch c { case "owner": if check.OwnerID != "" && pe.checker.UserID() == check.OwnerID { return true } case "org_member": if pe.checker.OrgID() != "" { return true } case "admin": if pe.checker.IsOrgAdmin() { return true } } } return false } // RequirePermission 要求特定权限 func (c *Checker) RequirePermission(resource, action string) error { pe := c.NewPolicyEvaluator() if !pe.HasPermission(PermissionCheck{ Resource: resource, Action: action, OrgID: c.OrgID(), }) { return vigo.ErrForbidden.WithString("permission denied: " + resource + ":" + action) } return nil } // Permission 基于策略的权限检查中间件 func Permission(resource, action string) func(*vigo.X) error { return func(x *vigo.X) error { checker := NewChecker(x) return checker.RequirePermission(resource, action) } } // PermissionWithOwner 带资源所有者检查的权限中间件 func PermissionWithOwner(resource, action, ownerIDKey string) func(*vigo.X) error { return func(x *vigo.X) error { checker := NewChecker(x) ownerID := "" if oid, ok := x.Get(ownerIDKey).(string); ok { ownerID = oid } if ownerID != "" && checker.UserID() == ownerID { return nil } return checker.RequirePermission(resource, action) } } // AdminOrOwner 管理员或所有者权限 func AdminOrOwner(ownerIDKey string) func(*vigo.X) error { return func(x *vigo.X) error { checker := NewChecker(x) if checker.IsOrgAdmin() { return nil } ownerID := "" if oid, ok := x.Get(ownerIDKey).(string); ok { ownerID = oid } if ownerID != "" && checker.UserID() == ownerID { return nil } return vigo.ErrForbidden.WithString("admin or owner permission required") } }