|
|
//
|
|
|
// Copyright (C) 2024 veypi <i@veypi.com>
|
|
|
// 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")
|
|
|
}
|
|
|
}
|