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.
OneAuth/internal/service/permission.go

350 lines
9.7 KiB
Go

2 weeks ago
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 检查权限
2 weeks ago
// 支持两种模式:
// 1. 组织上下文权限 (orgID != ""):检查用户在组织内的角色和权限
// 2. 个人上下文权限 (orgID == ""):检查用户的系统级权限和个人资源权限
2 weeks ago
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 weeks ago
// 2. 根据是否有组织上下文选择检查方式
2 weeks ago
if orgID != "" {
2 weeks ago
// 组织上下文:检查组织相关权限
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 weeks ago
}
}
2 weeks ago
// 2.2 获取用户在该组织的角色
2 weeks ago
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 weeks ago
// 2.3 获取角色关联的策略
2 weeks ago
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 weeks ago
// 2.4 收集所有策略ID
2 weeks ago
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 weeks ago
// 2.5 获取策略详情
2 weeks ago
var policies []model.Policy
if err := model.DB.Where("id IN ?", policyIDs).Find(&policies).Error; err != nil {
return nil, err
}
2 weeks ago
// 2.6 评估策略
2 weeks ago
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
}
2 weeks ago
// 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",
},
}
}
2 weeks ago
// 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()