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.
350 lines
9.7 KiB
Go
350 lines
9.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 检查权限
|
|
// 支持两种模式:
|
|
// 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()
|