//
// Copyright (C) 2024 veypi <i@veypi.com>
// 2025-02-14 16:08:06
// Distributed under terms of the MIT license.
//
package auth
import (
"context"
"errors"
"fmt"
"regexp"
"strings"
"time"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/cache"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
"github.com/veypi/vigo/contrib/event"
"gorm.io/gorm"
)
const (
// CtxKeyUserID 用户ID上下文键
CtxKeyUserID = "auth:user_id"
// CtxKeyOrgID 组织ID上下文键
CtxKeyOrgID = "auth:org_id"
// CtxKeyOrgRoles 组织角色上下文键
CtxKeyOrgRoles = "auth:org_roles"
// RoleCodeAdmin 管理员角色代码
RoleCodeAdmin = "admin"
// RoleCodeUser 普通用户角色代码
RoleCodeUser = "user"
)
// ========== 辅助函数 ==========
func GetUserID ( x * vigo . X ) string {
if userID , ok := x . Get ( CtxKeyUserID ) . ( string ) ; ok {
return userID
}
return ""
}
func GetOrgID ( x * vigo . X ) string {
if orgID , ok := x . Get ( CtxKeyOrgID ) . ( string ) ; ok {
return orgID
}
return ""
}
func GetOrgRoles ( x * vigo . X ) [ ] string {
if roles , ok := x . Get ( CtxKeyOrgRoles ) . ( [ ] string ) ; ok {
return roles
}
return nil
}
// Auth 权限管理接口
type Auth interface {
UserID ( x * vigo . X ) string
OrgID ( x * vigo . X ) string
// 加载组织信息 (中间件/手动调用)
LoadOrg ( x * vigo . X ) error
// ========== 中间件生成 ==========
// 基础权限检查
Perm ( permissionID string ) func ( * vigo . X ) error
// 特定资源权限检查 (自动从 Path/Query 获取资源ID)
PermOnResource ( permissionID , resourceKey string ) func ( * vigo . X ) error
// 满足任一权限
PermAny ( permissionIDs ... string ) func ( * vigo . X ) error
// 满足所有权限
PermAll ( permissionIDs ... string ) func ( * vigo . X ) error
// ========== 角色管理 ==========
// 添加角色定义
// policies 格式: "resource:action",例如 "user:read", "*:*"
AddRole ( roleCode , roleName string , policies ... string ) error
// ========== 权限管理 ==========
// 授予角色
GrantRole ( ctx context . Context , userID , orgID , roleCode string ) error
// 撤销角色
RevokeRole ( ctx context . Context , userID , orgID , roleCode string ) error
// 授予特定资源权限
GrantResourcePerm ( ctx context . Context , userID , orgID , permissionID , resourceID string ) error
// 撤销特定资源权限
RevokeResourcePerm ( ctx context . Context , userID , orgID , permissionID , resourceID string ) error
// 撤销用户所有权限
RevokeAll ( ctx context . Context , userID , orgID string ) error
// ========== 权限查询 ==========
// 检查权限
CheckPermission ( ctx context . Context , userID , orgID , permissionID , resourceID string ) bool
CheckPerm ( ctx context . Context , userID , orgID , permissionID , resourceID string ) bool
// 列出用户权限
ListUserPermissions ( ctx context . Context , userID , orgID string ) ( [ ] models . UserPermissionResult , error )
// 列出资源授权用户
ListResourceUsers ( ctx context . Context , orgID , permissionID , resourceID string ) ( [ ] models . ResourceUser , error )
}
// 全局 Auth 工厂
var Factory = & authFactory {
apps : make ( map [ string ] * appAuth ) ,
}
// VBaseAuth vbase 自身的权限管理实例
// 由 vbase 包在初始化时注入
var VBaseAuth = Factory . New ( "vb" )
func init ( ) {
// 为 VBaseAuth 添加默认角色
VBaseAuth . AddRole ( RoleCodeAdmin , "管理员" , "*:*" )
VBaseAuth . AddRole ( RoleCodeUser , "普通用户" ,
"user:read" ,
"org:read" ,
"org:create" ,
"oauth-client:read" ,
"oauth-client:create" ,
"oauth-client:update" ,
"oauth-client:delete" ,
)
// 注册权限初始化回调到 cfg 包
// 这样 models.InitDB() 可以在合适的时机调用,避免循环依赖
event . Add ( "vb.init.auth" , Factory . init )
}
type authFactory struct {
apps map [ string ] * appAuth // appKey -> auth实例
}
// New 创建权限管理实例(注册权限域)
func ( f * authFactory ) New ( scope string ) Auth {
if _ , exists := f . apps [ scope ] ; exists {
return f . apps [ scope ]
}
auth := & appAuth {
scope : scope ,
roleDefs : make ( map [ string ] roleDefinition ) ,
policies : make ( map [ string ] [ ] [ 2 ] string ) ,
roleInitDone : make ( map [ string ] bool ) ,
}
f . apps [ scope ] = auth
return auth
}
var validResourceRegex = regexp . MustCompile ( ` ^[a-zA-Z][a-zA-Z0-9_-]*$ ` )
func validatePermissionID ( permissionID string ) {
if permissionID == "*:*" {
return
}
parts := strings . Split ( permissionID , ":" )
// 允许 app:resource:action 或 resource:action 格式
// 如果是 app:resource:action, 则 parts 长度为 3
// 如果是 resource:action, 则 parts 长度为 2
if len ( parts ) != 2 && len ( parts ) != 3 {
panic ( fmt . Sprintf ( "invalid permission format: %s, expected 'resource:action' or 'app:resource:action'" , permissionID ) )
}
resource := parts [ len ( parts ) - 2 ]
if ! validResourceRegex . MatchString ( resource ) {
panic ( fmt . Sprintf ( "invalid resource identifier: %s, must start with letter and contain only letters, numbers, '-' or '_'" , resource ) )
}
}
// Init 初始化所有注册的权限配置
// - 检查不同 app 之间是否有冲突
// - 同步 Permission 到数据库
// - 建立预设角色
func ( f * authFactory ) init ( ) error {
for appKey , auth := range f . apps {
if err := auth . init ( ) ; err != nil {
return fmt . Errorf ( "failed to init auth for %s: %w" , appKey , err )
}
}
return nil
}
// roleDefinition 角色定义(内部使用)
type roleDefinition struct {
code string
name string
description string
policies [ ] string // 权限列表: ["resource:action", "*:*"]
}
// appAuth 单个权限域的权限管理
type appAuth struct {
scope string // 权限域标识
roleDefs map [ string ] roleDefinition // roleCode -> role definition
policies map [ string ] [ ] [ 2 ] string // roleCode -> list of [resource, action] pairs
roleInitDone map [ string ] bool // roleCode -> whether role is initialized in DB
}
// AddRole 添加角色定义
// policies 格式: "resource:action",例如 "user:read", "*:*"
func ( a * appAuth ) AddRole ( roleCode , roleName string , policies ... string ) error {
if roleCode == "" || roleName == "" {
return fmt . Errorf ( "role code and name cannot be empty" )
}
// 解析并验证权限格式
parsedPolicies := make ( [ ] [ 2 ] string , 0 , len ( policies ) )
for _ , policy := range policies {
// 严格检查格式: resource:action
parts := strings . Split ( policy , ":" )
if len ( parts ) != 2 {
return fmt . Errorf ( "invalid policy format: %s, expected 'resource:action'" , policy )
}
resource , action := parts [ 0 ] , parts [ 1 ]
// 验证 resource 和 action 不为空
if resource == "" || action == "" {
return fmt . Errorf ( "resource and action cannot be empty in policy: %s" , policy )
}
// 验证 resource 格式(如果不是通配符)
if resource != "*" {
if ! validResourceRegex . MatchString ( resource ) {
return fmt . Errorf ( "invalid resource identifier: %s in policy: %s, must start with letter and contain only letters, numbers, '-' or '_'" , resource , policy )
}
}
// 验证 action 格式(如果不是通配符)
if action != "*" {
if ! validResourceRegex . MatchString ( action ) {
return fmt . Errorf ( "invalid action identifier: %s in policy: %s, must start with letter and contain only letters, numbers, '-' or '_'" , action , policy )
}
}
parsedPolicies = append ( parsedPolicies , [ 2 ] string { resource , action } )
}
// 存储角色定义
a . roleDefs [ roleCode ] = roleDefinition {
code : roleCode ,
name : roleName ,
}
a . policies [ roleCode ] = parsedPolicies
// 如果已经初始化过,立即同步到数据库
if len ( a . roleInitDone ) > 0 {
return a . initRole ( roleCode )
}
return nil
}
// init 初始化应用的权限配置
func ( a * appAuth ) init ( ) error {
// 1. 同步权限定义到数据库
for _ , permDef := range a . extractPermissions ( ) {
var perm models . Permission
err := cfg . DB ( ) . Where ( "id = ?" , permDef . ID ) . First ( & perm ) . Error
if err != nil {
// 不存在则创建
perm = permDef
if err := cfg . DB ( ) . Create ( & perm ) . Error ; err != nil {
return fmt . Errorf ( "failed to create permission %s: %w" , permDef . ID , err )
}
}
}
// 2. 创建系统预设角色(跳过 _app_info)
for roleCode := range a . roleDefs {
if roleCode == "_app_info" {
continue
}
if err := a . initRole ( roleCode ) ; err != nil {
return err
}
}
return nil
}
// extractPermissions 从角色定义中提取所有权限
func ( a * appAuth ) extractPermissions ( ) [ ] models . Permission {
permMap := make ( map [ string ] models . Permission )
for roleCode , policies := range a . policies {
if roleCode == "_app_info" {
continue
}
for _ , policy := range policies {
resource , action := policy [ 0 ] , policy [ 1 ]
// 跳过通配符权限的特殊处理
if resource == "*" && action == "*" {
continue
}
permID := fmt . Sprintf ( "%s:%s:%s" , a . scope , resource , action )
if _ , exists := permMap [ permID ] ; ! exists {
permMap [ permID ] = models . Permission {
ID : permID ,
Scope : a . scope ,
Resource : resource ,
Action : action ,
Description : fmt . Sprintf ( "%s %s on %s" , a . scope , action , resource ) ,
}
}
}
}
result := make ( [ ] models . Permission , 0 , len ( permMap ) )
for _ , perm := range permMap {
result = append ( result , perm )
}
return result
}
// initRole 初始化系统预设角色
func ( a * appAuth ) initRole ( roleCode string ) error {
roleDef , exists := a . roleDefs [ roleCode ]
if ! exists {
return fmt . Errorf ( "role not found: %s" , roleCode )
}
policies , hasPolicies := a . policies [ roleCode ]
if ! hasPolicies {
policies = [ ] [ 2 ] string { }
}
// 查找或创建系统角色
var role models . Role
err := cfg . DB ( ) . Where ( "code = ? AND org_id IS NULL" , roleDef . code ) . First ( & role ) . Error
if err != nil {
// 创建新角色
role = models . Role {
OrgID : nil ,
Code : roleDef . code ,
Name : roleDef . name ,
Description : roleDef . description ,
IsSystem : true ,
Status : 1 ,
}
if err := cfg . DB ( ) . Create ( & role ) . Error ; err != nil {
return fmt . Errorf ( "failed to create role %s: %w" , roleDef . code , err )
}
}
// 同步角色权限
hasWildcard := false
for _ , policy := range policies {
resource , action := policy [ 0 ] , policy [ 1 ]
// 处理通配符权限
if resource == "*" && action == "*" {
hasWildcard = true
continue
}
permID := fmt . Sprintf ( "%s:%s:%s" , a . scope , resource , action )
// 检查关联是否存在
var count int64
cfg . DB ( ) . Model ( & models . RolePermission { } ) .
Where ( "role_id = ? AND permission_id = ?" , role . ID , permID ) .
Count ( & count )
if count == 0 {
rp := models . RolePermission {
RoleID : role . ID ,
PermissionID : permID ,
Condition : "none" ,
}
if err := cfg . DB ( ) . Create ( & rp ) . Error ; err != nil {
return fmt . Errorf ( "failed to create role permission: %w" , err )
}
}
}
// 为通配符权限创建记录
if hasWildcard {
wildcardPermID := fmt . Sprintf ( "%s:*:*" , a . scope )
// 先确保通配符 permission 存在
var perm models . Permission
err := cfg . DB ( ) . Where ( "id = ?" , wildcardPermID ) . First ( & perm ) . Error
if err != nil {
// 创建通配符 permission
perm = models . Permission {
ID : wildcardPermID ,
Scope : a . scope ,
Resource : "*" ,
Action : "*" ,
Description : fmt . Sprintf ( "%s wildcard permission" , a . scope ) ,
}
if err := cfg . DB ( ) . Create ( & perm ) . Error ; err != nil {
return fmt . Errorf ( "failed to create wildcard permission: %w" , err )
}
}
// 创建 role_permission 关联
var count int64
cfg . DB ( ) . Model ( & models . RolePermission { } ) .
Where ( "role_id = ? AND permission_id = ?" , role . ID , wildcardPermID ) .
Count ( & count )
if count == 0 {
rp := models . RolePermission {
RoleID : role . ID ,
PermissionID : wildcardPermID ,
Condition : "none" ,
}
if err := cfg . DB ( ) . Create ( & rp ) . Error ; err != nil {
return fmt . Errorf ( "failed to create wildcard role permission: %w" , err )
}
}
}
a . roleInitDone [ roleCode ] = true
return nil
}
// ========== 中间件实现 ==========
func ( a * appAuth ) UserID ( x * vigo . X ) string {
return GetUserID ( x )
}
func ( a * appAuth ) OrgID ( x * vigo . X ) string {
return GetOrgID ( x )
}
func ( a * appAuth ) LoadOrg ( x * vigo . X ) error {
orgID := x . Request . Header . Get ( "X-Org-ID" )
if orgID == "" {
orgID = x . Request . URL . Query ( ) . Get ( "org_id" )
}
if orgID == "" {
orgID = x . PathParams . Get ( "org_id" )
}
if orgID == "" {
// 没有指定组织
return vigo . ErrInvalidArg . WithString ( "missing org_id" )
}
userID := GetUserID ( x )
if userID == "" {
return vigo . ErrUnauthorized
}
// 检查用户是否为组织成员
var member models . OrgMember
err := cfg . DB ( ) . Where ( "user_id = ? AND org_id = ? AND status = ?" , userID , orgID , models . MemberStatusActive ) . First ( & member ) . Error
if err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
return vigo . ErrForbidden . WithString ( "not a member of this organization" )
}
return vigo . ErrInternalServer . WithError ( err )
}
x . Set ( CtxKeyOrgID , orgID )
return nil
}
func ( a * appAuth ) Perm ( permissionID string ) func ( * vigo . X ) error {
validatePermissionID ( permissionID )
return func ( x * vigo . X ) error {
userID := GetUserID ( x )
if userID == "" {
return vigo . ErrUnauthorized
}
orgID := GetOrgID ( x )
if err := a . checkPermission ( x . Context ( ) , userID , orgID , permissionID , "" ) ; err != nil {
return err
}
return nil
}
}
// PermOnResource 检查当前用户对特定资源实例是否有指定权限 (ACL)
//
// 鉴权逻辑:
// 1. 全局权限检查: 如果用户拥有全局权限 (如 "user:*", "*:*"),直接通过。
// 2. 实例权限检查: 检查 user_permissions 表中是否有 (permissionID, resourceID) 的记录。
//
// 最佳实践:
// - 配合 GrantResourcePerm 使用: 在创建资源时,必须显式赋予创建者权限。
// - 适用于高价值、需共享的资源 (如 User, Org, Project)。
// - 对于私有/高频资源 (如 Order, Log),建议使用 Manual Check (在业务逻辑中直接检查 OwnerID)。
func ( a * appAuth ) PermOnResource ( permissionID , resourceKey string ) func ( * vigo . X ) error {
validatePermissionID ( permissionID )
return func ( x * vigo . X ) error {
userID := GetUserID ( x )
if userID == "" {
return vigo . ErrUnauthorized
}
orgID := GetOrgID ( x )
// 尝试从 PathParams 获取
resourceID := x . PathParams . Get ( resourceKey )
if resourceID == "" {
// 尝试从 Query 获取
resourceID = x . Request . URL . Query ( ) . Get ( resourceKey )
}
if err := a . checkPermission ( x . Context ( ) , userID , orgID , permissionID , resourceID ) ; err != nil {
return err
}
return nil
}
}
// 内部辅助检查方法,返回 error 以便于统一处理错误响应
func ( a * appAuth ) checkPermission ( ctx context . Context , userID , orgID , permissionID , resourceID string ) error {
ok , err := a . checkPermissionDB ( ctx , userID , orgID , permissionID , resourceID )
if err != nil {
return vigo . ErrInternalServer . WithError ( err )
}
if ! ok {
return vigo . ErrForbidden
}
return nil
}
func ( a * appAuth ) PermAny ( permissionIDs ... string ) func ( * vigo . X ) error {
for _ , pid := range permissionIDs {
validatePermissionID ( pid )
}
return func ( x * vigo . X ) error {
userID := GetUserID ( x )
if userID == "" {
return vigo . ErrUnauthorized
}
orgID := GetOrgID ( x )
for _ , pid := range permissionIDs {
if err := a . checkPermission ( x . Context ( ) , userID , orgID , pid , "" ) ; err == nil {
return nil
}
}
return vigo . ErrNoPermission
}
}
func ( a * appAuth ) PermAll ( permissionIDs ... string ) func ( * vigo . X ) error {
for _ , pid := range permissionIDs {
validatePermissionID ( pid )
}
return func ( x * vigo . X ) error {
userID := GetUserID ( x )
if userID == "" {
return vigo . ErrUnauthorized
}
orgID := GetOrgID ( x )
for _ , pid := range permissionIDs {
if err := a . checkPermission ( x . Context ( ) , userID , orgID , pid , "" ) ; err != nil {
return err
}
}
return nil
}
}
// ========== 权限管理实现 ==========
func ( a * appAuth ) GrantRole ( ctx context . Context , userID , orgID , roleCode string ) error {
// 查找角色
var role models . Role
query := cfg . DB ( ) . Where ( "code = ?" , roleCode )
if orgID != "" {
query = query . Where ( "org_id = ?" , orgID )
} else {
query = query . Where ( "org_id IS NULL" )
}
if err := query . First ( & role ) . Error ; err != nil {
// 如果指定了 OrgID 但没找到,尝试查找全局角色
if orgID != "" {
query = cfg . DB ( ) . Where ( "code = ? AND org_id IS NULL" , roleCode )
if err := query . First ( & role ) . Error ; err != nil {
return fmt . Errorf ( "role not found: %s" , roleCode )
}
} else {
return fmt . Errorf ( "role not found: %s" , roleCode )
}
}
// 检查是否已存在
var count int64
roleQuery := cfg . DB ( ) . Model ( & models . UserRole { } ) .
Where ( "user_id = ? AND role_id = ?" , userID , role . ID )
if orgID != "" {
roleQuery = roleQuery . Where ( "org_id = ?" , orgID )
} else {
roleQuery = roleQuery . Where ( "org_id IS NULL" )
}
roleQuery . Count ( & count )
if count > 0 {
return nil // 已存在
}
var orgIDPtr * string
if orgID != "" {
orgIDPtr = & orgID
}
userRole := models . UserRole {
UserID : userID ,
OrgID : orgIDPtr ,
RoleID : role . ID ,
ExpireAt : nil , // 默认不过期
}
if err := cfg . DB ( ) . Create ( & userRole ) . Error ; err != nil {
return err
}
incUserPermVersion ( userID )
return nil
}
func ( a * appAuth ) RevokeRole ( ctx context . Context , userID , orgID , roleCode string ) error {
var role models . Role
// 优先查找组织特定角色
query := cfg . DB ( ) . Where ( "code = ?" , roleCode )
if orgID != "" {
query = query . Where ( "org_id = ?" , orgID )
} else {
query = query . Where ( "org_id IS NULL" )
}
if err := query . First ( & role ) . Error ; err != nil {
// 如果没找到,尝试查找全局角色
if orgID != "" {
if err := cfg . DB ( ) . Where ( "code = ? AND org_id IS NULL" , roleCode ) . First ( & role ) . Error ; err != nil {
return nil // 角色不存在,无需撤销
}
} else {
return nil
}
}
// 构建删除条件
deleteQuery := cfg . DB ( ) . Where ( "user_id = ? AND role_id = ?" , userID , role . ID )
if orgID != "" {
deleteQuery = deleteQuery . Where ( "org_id = ?" , orgID )
} else {
deleteQuery = deleteQuery . Where ( "org_id IS NULL" )
}
if err := deleteQuery . Delete ( & models . UserRole { } ) . Error ; err != nil {
return err
}
incUserPermVersion ( userID )
return nil
}
func ( a * appAuth ) GrantResourcePerm ( ctx context . Context , userID , orgID , permissionID , resourceID string ) error {
if strings . Count ( permissionID , ":" ) == 1 {
permissionID = fmt . Sprintf ( "%s:%s" , a . scope , permissionID )
}
// 检查权限是否存在
var perm models . Permission
if err := cfg . DB ( ) . Where ( "id = ?" , permissionID ) . First ( & perm ) . Error ; err != nil {
return fmt . Errorf ( "permission not found: %s" , permissionID )
}
// 检查是否已存在
var existing models . UserPermission
query := cfg . DB ( ) . Where ( "user_id = ? AND permission_id = ? AND resource_id = ?" ,
userID , permissionID , resourceID )
if orgID != "" {
query = query . Where ( "org_id = ?" , orgID )
} else {
query = query . Where ( "org_id IS NULL" )
}
err := query . First ( & existing ) . Error
if err == nil {
// 已存在
return nil
}
var orgIDPtr * string
if orgID != "" {
orgIDPtr = & orgID
}
userPerm := models . UserPermission {
UserID : userID ,
OrgID : orgIDPtr ,
PermissionID : permissionID ,
ResourceID : resourceID ,
ExpireAt : nil , // 默认不过期
GrantedBy : "" , // 默认空
}
if err := cfg . DB ( ) . Create ( & userPerm ) . Error ; err != nil {
return err
}
incUserPermVersion ( userID )
return nil
}
func ( a * appAuth ) RevokeResourcePerm ( ctx context . Context , userID , orgID , permissionID , resourceID string ) error {
if strings . Count ( permissionID , ":" ) == 1 {
permissionID = fmt . Sprintf ( "%s:%s" , a . scope , permissionID )
}
query := cfg . DB ( ) . Where ( "user_id = ? AND permission_id = ? AND resource_id = ?" ,
userID , permissionID , resourceID )
if orgID != "" {
query = query . Where ( "org_id = ?" , orgID )
} else {
query = query . Where ( "org_id IS NULL" )
}
if err := query . Delete ( & models . UserPermission { } ) . Error ; err != nil {
return err
}
incUserPermVersion ( userID )
return nil
}
func ( a * appAuth ) RevokeAll ( ctx context . Context , userID , orgID string ) error {
// 删除用户角色
roleQuery := cfg . DB ( ) . Where ( "user_id = ?" , userID )
if orgID != "" {
roleQuery = roleQuery . Where ( "org_id = ?" , orgID )
} else {
roleQuery = roleQuery . Where ( "org_id IS NULL" )
}
if err := roleQuery . Delete ( & models . UserRole { } ) . Error ; err != nil {
return err
}
// 删除用户特定权限
permQuery := cfg . DB ( ) . Where ( "user_id = ?" , userID )
if orgID != "" {
permQuery = permQuery . Where ( "org_id = ?" , orgID )
} else {
permQuery = permQuery . Where ( "org_id IS NULL" )
}
if err := permQuery . Delete ( & models . UserPermission { } ) . Error ; err != nil {
return err
}
incUserPermVersion ( userID )
return nil
}
// ========== 权限查询实现 ==========
func ( a * appAuth ) CheckPermission ( ctx context . Context , userID , orgID , permissionID , resourceID string ) bool {
ok , _ := a . checkPermissionDB ( ctx , userID , orgID , permissionID , resourceID )
return ok
}
func ( a * appAuth ) CheckPerm ( ctx context . Context , userID , orgID , permissionID , resourceID string ) bool {
return a . CheckPermission ( ctx , userID , orgID , permissionID , resourceID )
}
func ( a * appAuth ) checkPermissionDB ( ctx context . Context , userID , orgID , permissionID , resourceID string ) ( bool , error ) {
// 1. 检查用户是否有该权限的角色(包括当前组织角色和系统全局角色)
var roleIDs [ ] string
roleQuery := cfg . DB ( ) . Model ( & models . UserRole { } ) .
Where ( "user_id = ? AND (expire_at IS NULL OR expire_at > ?)" , userID , time . Now ( ) )
if orgID != "" {
roleQuery = roleQuery . Where ( "org_id = ? OR org_id IS NULL" , orgID )
} else {
roleQuery = roleQuery . Where ( "org_id IS NULL" )
}
if err := roleQuery . Pluck ( "role_id" , & roleIDs ) . Error ; err != nil {
return false , err
}
if len ( roleIDs ) > 0 {
// 构造可能的通配符权限ID
permsToCheck := [ ] string { permissionID }
parts := strings . Split ( permissionID , ":" )
if len ( parts ) == 3 {
// app:resource:*
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:%s:*" , parts [ 0 ] , parts [ 1 ] ) )
// app:*:*
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:*:*" , parts [ 0 ] ) )
} else if len ( parts ) == 2 {
// resource:action -> appKey:resource:action
fullPermID := fmt . Sprintf ( "%s:%s" , a . scope , permissionID )
permsToCheck = append ( permsToCheck , fullPermID )
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:%s:*" , a . scope , parts [ 0 ] ) )
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:*:*" , a . scope ) )
}
// 检查这些角色是否有所需权限
var count int64
if err := cfg . DB ( ) . Model ( & models . RolePermission { } ) .
Where ( "role_id IN ? AND permission_id IN ?" , roleIDs , permsToCheck ) .
Count ( & count ) . Error ; err != nil {
return false , err
}
if count > 0 {
return true , nil
}
}
// 2. 检查用户是否有特定的资源权限
// 构造可能的通配符权限ID (同上)
permsToCheck := [ ] string { permissionID }
parts := strings . Split ( permissionID , ":" )
if len ( parts ) == 3 {
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:%s:*" , parts [ 0 ] , parts [ 1 ] ) )
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:*:*" , parts [ 0 ] ) )
} else if len ( parts ) == 2 {
// resource:action -> appKey:resource:action
fullPermID := fmt . Sprintf ( "%s:%s" , a . scope , permissionID )
permsToCheck = append ( permsToCheck , fullPermID )
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:%s:*" , a . scope , parts [ 0 ] ) )
permsToCheck = append ( permsToCheck , fmt . Sprintf ( "%s:*:*" , a . scope ) )
}
var userPermCount int64
query := cfg . DB ( ) . Model ( & models . UserPermission { } ) .
Where ( "user_id = ? AND permission_id IN ? AND (expire_at IS NULL OR expire_at > ?)" ,
userID , permsToCheck , time . Now ( ) )
if orgID != "" {
query = query . Where ( "org_id = ?" , orgID )
} else {
query = query . Where ( "org_id IS NULL" )
}
if resourceID != "" {
query = query . Where ( "resource_id = ? OR resource_id = '*'" , resourceID )
} else {
query = query . Where ( "resource_id = '*'" )
}
if err := query . Count ( & userPermCount ) . Error ; err != nil {
return false , err
}
return userPermCount > 0 , nil
}
func ( a * appAuth ) ListUserPermissions ( ctx context . Context , userID , orgID string ) ( [ ] models . UserPermissionResult , error ) {
result := make ( [ ] models . UserPermissionResult , 0 )
// 1. 获取用户角色对应的权限
var roleIDs [ ] string
if err := cfg . DB ( ) . Model ( & models . UserRole { } ) .
Where ( "user_id = ? AND org_id = ? AND (expire_at IS NULL OR expire_at > ?)" ,
userID , orgID , time . Now ( ) ) .
Pluck ( "role_id" , & roleIDs ) . Error ; err != nil {
return nil , err
}
if len ( roleIDs ) > 0 {
var permIDs [ ] string
if err := cfg . DB ( ) . Model ( & models . RolePermission { } ) .
Where ( "role_id IN ?" , roleIDs ) .
Pluck ( "permission_id" , & permIDs ) . Error ; err != nil {
return nil , err
}
for _ , permID := range permIDs {
result = append ( result , models . UserPermissionResult {
PermissionID : permID ,
ResourceID : "*" ,
Actions : [ ] string { "*" } ,
} )
}
}
// 2. 获取用户特定资源权限
var userPerms [ ] models . UserPermission
if err := cfg . DB ( ) . Where ( "user_id = ? AND org_id = ? AND (expire_at IS NULL OR expire_at > ?)" ,
userID , orgID , time . Now ( ) ) .
Find ( & userPerms ) . Error ; err != nil {
return nil , err
}
for _ , up := range userPerms {
result = append ( result , models . UserPermissionResult {
PermissionID : up . PermissionID ,
ResourceID : up . ResourceID ,
Actions : [ ] string { "*" } ,
} )
}
return result , nil
}
func ( a * appAuth ) ListResourceUsers ( ctx context . Context , orgID , permissionID , resourceID string ) ( [ ] models . ResourceUser , error ) {
result := make ( [ ] models . ResourceUser , 0 )
// 查询有该资源权限的用户
var userPerms [ ] models . UserPermission
query := cfg . DB ( ) . Where ( "org_id = ? AND permission_id = ?" , orgID , permissionID )
if resourceID != "" {
query = query . Where ( "resource_id = ? OR resource_id = '*'" , resourceID )
}
if err := query . Find ( & userPerms ) . Error ; err != nil {
return nil , err
}
userMap := make ( map [ string ] [ ] string )
for _ , up := range userPerms {
userMap [ up . UserID ] = append ( userMap [ up . UserID ] , "*" )
}
for userID , actions := range userMap {
result = append ( result , models . ResourceUser {
UserID : userID ,
Actions : actions ,
} )
}
return result , nil
}
// ========== 辅助方法 ==========
func ( a * appAuth ) isAdmin ( ctx context . Context , userID , orgID string ) ( bool , error ) {
// 检查用户是否有管理员角色
var adminRoleIDs [ ] string
if err := cfg . DB ( ) . Model ( & models . Role { } ) .
Where ( "code = ?" , RoleCodeAdmin ) .
Pluck ( "id" , & adminRoleIDs ) . Error ; err != nil {
return false , err
}
if len ( adminRoleIDs ) == 0 {
return false , nil
}
var count int64
if err := cfg . DB ( ) . Model ( & models . UserRole { } ) .
Where ( "user_id = ? AND org_id = ? AND role_id IN ?" , userID , orgID , adminRoleIDs ) .
Count ( & count ) . Error ; err != nil {
return false , err
}
return count > 0 , nil
}
// ========== Cache Helpers ==========
func getUserPermVersion ( userID string ) string {
if ! cache . IsEnabled ( ) {
return "0"
}
key := fmt . Sprintf ( "auth:user_ver:%s" , userID )
ver , err := cache . Client . Get ( cache . Ctx , key ) . Result ( )
if err != nil {
return "0"
}
return ver
}
func incUserPermVersion ( userID string ) {
if ! cache . IsEnabled ( ) {
return
}
key := fmt . Sprintf ( "auth:user_ver:%s" , userID )
cache . Client . Incr ( cache . Ctx , key )
}