mirror of https://github.com/veypi/OneAuth.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
6.7 KiB
Go
259 lines
6.7 KiB
Go
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()
|