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 检查权限 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 != "" { 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 } } } // 3. 获取用户在该组织的角色 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 } // 4. 获取角色关联的策略 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 } // 5. 收集所有策略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) } // 6. 获取策略详情 var policies []model.Policy if err := model.DB.Where("id IN ?", policyIDs).Find(&policies).Error; err != nil { return nil, err } // 7. 评估策略 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 } // 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()