package service import ( "fmt" "strings" "time" "github.com/veypi/vbase/internal/cache" "github.com/veypi/vbase/internal/model" ) // PermissionChecker 权限检查器 type PermissionChecker struct{} // NewPermissionChecker 创建权限检查器 func NewPermissionChecker() *PermissionChecker { return &PermissionChecker{} } // CheckResult 检查结果 type CheckResult struct { Allowed bool `json:"allowed"` Reason string `json:"reason,omitempty"` } // Check 检查权限 // 支持两种模式: // 1. 组织上下文权限 (orgID != ""):检查用户在组织内的角色和权限 // 2. 个人上下文权限 (orgID == ""):检查用户的系统级权限和个人资源权限 func (pc *PermissionChecker) Check(userID, orgID, resource, action string, resourceData map[string]any) (*CheckResult, error) { // 1. 检查缓存 if cache.IsEnabled() { allowed, cached, err := cache.GetPermission(userID, orgID, resource, action) if err == nil && cached { return &CheckResult{Allowed: allowed}, nil } } // 2. 根据是否有组织上下文选择检查方式 if orgID != "" { // 组织上下文:检查组织相关权限 return pc.checkOrgPermission(userID, orgID, resource, action, resourceData) } // 3. 无组织上下文:检查个人/系统级权限 return pc.checkPersonalPermission(userID, resource, action, resourceData) } // checkOrgPermission 检查组织上下文权限 func (pc *PermissionChecker) checkOrgPermission(userID, orgID, resource, action string, resourceData map[string]any) (*CheckResult, error) { // 2.1 检查是否是组织所有者(拥有所有权限) var org model.Org if err := model.DB.First(&org, "id = ?", orgID).Error; err == nil { if org.OwnerID == userID { pc.cacheResult(userID, orgID, resource, action, true) return &CheckResult{Allowed: true, Reason: "organization owner"}, nil } } // 2.2 获取用户在该组织的角色 var member model.OrgMember if err := model.DB.Where("org_id = ? AND user_id = ? AND status = ?", orgID, userID, model.MemberStatusActive).First(&member).Error; err != nil { // 用户不在组织中 pc.cacheResult(userID, orgID, resource, action, false) return &CheckResult{Allowed: false, Reason: "not a member of organization"}, nil } // 2.3 获取角色关联的策略 roleIDs := parseRoles(member.RoleIDs) if len(roleIDs) == 0 { pc.cacheResult(userID, orgID, resource, action, false) return &CheckResult{Allowed: false, Reason: "no roles assigned"}, nil } var roles []model.Role if err := model.DB.Where("id IN ?", roleIDs).Find(&roles).Error; err != nil { return nil, err } // 2.4 收集所有策略ID policyIDMap := make(map[string]bool) for _, role := range roles { ids := parseRoles(role.PolicyIDs) for _, id := range ids { policyIDMap[id] = true } } if len(policyIDMap) == 0 { pc.cacheResult(userID, orgID, resource, action, false) return &CheckResult{Allowed: false, Reason: "no policies assigned"}, nil } policyIDs := make([]string, 0, len(policyIDMap)) for id := range policyIDMap { policyIDs = append(policyIDs, id) } // 2.5 获取策略详情 var policies []model.Policy if err := model.DB.Where("id IN ?", policyIDs).Find(&policies).Error; err != nil { return nil, err } // 2.6 评估策略 allowed := pc.evaluatePolicies(policies, userID, orgID, resource, action, resourceData) pc.cacheResult(userID, orgID, resource, action, allowed) if allowed { return &CheckResult{Allowed: true}, nil } return &CheckResult{Allowed: false, Reason: "policy denied"}, nil } // checkPersonalPermission 检查个人/系统级权限(无组织上下文) func (pc *PermissionChecker) checkPersonalPermission(userID, resource, action string, resourceData map[string]any) (*CheckResult, error) { // 3.1 检查是否是资源所有者 // 对于个人资源(如自己的用户信息),允许操作 if resourceData != nil { if ownerID, ok := resourceData["owner_id"].(string); ok && ownerID == userID { pc.cacheResult(userID, "", resource, action, true) return &CheckResult{Allowed: true, Reason: "resource owner"}, nil } if createdBy, ok := resourceData["created_by"].(string); ok && createdBy == userID { pc.cacheResult(userID, "", resource, action, true) return &CheckResult{Allowed: true, Reason: "resource creator"}, nil } } // 3.2 加载系统级默认策略 // 无组织用户可以执行的操作:管理自己的数据 policies := pc.getDefaultPersonalPolicies() // 3.3 评估策略 allowed := pc.evaluatePolicies(policies, userID, "", resource, action, resourceData) pc.cacheResult(userID, "", resource, action, allowed) if allowed { return &CheckResult{Allowed: true}, nil } return &CheckResult{Allowed: false, Reason: "personal policy denied"}, nil } // getDefaultPersonalPolicies 获取个人默认策略 // 无组织用户默认可以管理自己的资源 func (pc *PermissionChecker) getDefaultPersonalPolicies() []model.Policy { return []model.Policy{ { Code: "personal:user:read:own", Resource: "user", Action: "read", Effect: model.EffectAllow, Condition: "owner", }, { Code: "personal:user:update:own", Resource: "user", Action: "update", Effect: model.EffectAllow, Condition: "owner", }, { Code: "personal:user:delete:own", Resource: "user", Action: "delete", Effect: model.EffectAllow, Condition: "owner", }, { Code: "personal:binding:manage", Resource: "binding", Action: "*", Effect: model.EffectAllow, Condition: "", }, { Code: "personal:org:create", Resource: "org", Action: "create", Effect: model.EffectAllow, Condition: "", }, { Code: "personal:org:read:own", Resource: "org", Action: "read", Effect: model.EffectAllow, Condition: "owner", }, } } // evaluatePolicies 评估策略 func (pc *PermissionChecker) evaluatePolicies(policies []model.Policy, userID, orgID, resource, action string, resourceData map[string]any) bool { // 先处理deny策略 for _, p := range policies { if p.Effect != model.EffectDeny { continue } if pc.matchPolicy(&p, resource, action) { // 检查条件 if pc.evaluateCondition(&p, userID, orgID, resourceData) { return false } } } // 再处理allow策略 for _, p := range policies { if p.Effect != model.EffectAllow { continue } if pc.matchPolicy(&p, resource, action) { if pc.evaluateCondition(&p, userID, orgID, resourceData) { return true } } } return false } // matchPolicy 匹配策略资源和方法 func (pc *PermissionChecker) matchPolicy(p *model.Policy, resource, action string) bool { // 资源匹配 if p.Resource != "*" && p.Resource != resource { return false } // 动作匹配 if p.Action != "*" && p.Action != action { return false } return true } // evaluateCondition 评估条件 func (pc *PermissionChecker) evaluateCondition(p *model.Policy, userID, orgID string, resourceData map[string]any) bool { condition := p.Condition if condition == "" || condition == "true" { return true } // 简单条件评估 switch condition { case "owner": // 检查是否是资源所有者 if ownerID, ok := resourceData["owner_id"].(string); ok { return ownerID == userID } if createdBy, ok := resourceData["created_by"].(string); ok { return createdBy == userID } return false case "org_member": // 检查是否是组织成员 if orgID == "" { return false } var count int64 model.DB.Model(&model.OrgMember{}).Where("org_id = ? AND user_id = ? AND status = ?", orgID, userID, model.MemberStatusActive).Count(&count) return count > 0 default: // 其他复杂条件暂时返回true return true } } // cacheResult 缓存结果 func (pc *PermissionChecker) cacheResult(userID, orgID, resource, action string, allowed bool) { if cache.IsEnabled() { cache.SetPermission(userID, orgID, resource, action, allowed, 1*time.Minute) } } // GetUserPermissions 获取用户的所有权限 func (pc *PermissionChecker) GetUserPermissions(userID, orgID string) ([]string, error) { // 获取用户角色 var member model.OrgMember if err := model.DB.Where("org_id = ? AND user_id = ? AND status = ?", orgID, userID, model.MemberStatusActive).First(&member).Error; err != nil { return []string{}, nil } roleIDs := parseRoles(member.RoleIDs) if len(roleIDs) == 0 { return []string{}, nil } var roles []model.Role if err := model.DB.Where("id IN ?", roleIDs).Find(&roles).Error; err != nil { return nil, err } // 收集策略 policyIDMap := make(map[string]bool) for _, role := range roles { ids := parseRoles(role.PolicyIDs) for _, id := range ids { policyIDMap[id] = true } } if len(policyIDMap) == 0 { return []string{}, nil } policyIDs := make([]string, 0, len(policyIDMap)) for id := range policyIDMap { policyIDs = append(policyIDs, id) } var policies []model.Policy if err := model.DB.Where("id IN ?", policyIDs).Find(&policies).Error; err != nil { return nil, err } // 格式化权限 perms := make([]string, 0, len(policies)) for _, p := range policies { if p.Effect == model.EffectAllow { perms = append(perms, fmt.Sprintf("%s:%s", p.Resource, p.Action)) } } return perms, nil } // ClearUserPermissionCache 清除用户权限缓存 func ClearUserPermissionCache(userID string) { if cache.IsEnabled() { cache.DeleteUserPermissions(userID) } } // ClearOrgPermissionCache 清除组织权限缓存 func ClearOrgPermissionCache(orgID string) { if cache.IsEnabled() { cache.DeleteOrgPermissions(orgID) } } func parseRoles(roleIDs string) []string { if roleIDs == "" { return []string{} } return strings.Split(roleIDs, ",") } // InitPermissionChecker 初始化权限检查器 var InitPermissionChecker = NewPermissionChecker()