refactor(auth): Migrate to new vigo auth.Auth and Provider pattern

- Rename appAuth to vbaseProvider implementing auth.Provider interface
    - Replace auth.VBaseAuth with cfg.Auth (auth.Auth struct) for middleware
    - Add global cfg.Auth instance with SetProvider injection in init.go
    - Update all API handlers to use cfg.Auth.RequireXxx instead of PermXxx
    - Update tests to use cfg.Auth for permission checks
    - Remove Login/Perm methods from Provider (now in auth.Auth struct)
master
veypi 2 months ago
parent c79cd1f2d5
commit 3ea5499532

@ -15,6 +15,7 @@ import (
"github.com/veypi/vbase/libs/jwt"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
"github.com/veypi/vigo/contrib/requestmeta"
)
// LoginRequest 登录请求
@ -191,8 +192,9 @@ func login(x *vigo.X, req *LoginRequest) (*AuthResponse, error) {
TokenID: getJTI(tokenPair.AccessToken),
Type: "access",
DeviceInfo: x.Request.UserAgent(),
IP: x.GetRemoteIP(),
ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry),
IP: requestmeta.RemoteIP(x),
ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry),
}
cfg.DB().Create(session)
@ -268,7 +270,7 @@ func refresh(x *vigo.X, req *RefreshRequest) (*AuthResponse, error) {
TokenID: getJTI(tokenPair.AccessToken),
Type: "access",
DeviceInfo: x.Request.UserAgent(),
IP: x.GetRemoteIP(),
IP: requestmeta.RemoteIP(x),
ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry),
}
cfg.DB().Create(session)

@ -7,7 +7,6 @@
package auth
import (
baseAuth "github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/crypto"
"github.com/veypi/vbase/models"
@ -35,7 +34,7 @@ type UserInfoWithPerms struct {
// me 获取当前用户信息
func me(x *vigo.X) (*UserInfoWithPerms, error) {
userID := baseAuth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return nil, vigo.ErrUnauthorized
}
@ -96,7 +95,7 @@ type UpdateMeRequest struct {
// updateMe 更新当前用户信息
func updateMe(x *vigo.X, req *UpdateMeRequest) (*UserInfoWithPerms, error) {
userID := baseAuth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return nil, vigo.ErrUnauthorized
}
@ -136,7 +135,7 @@ type ChangePasswordRequest struct {
// changePassword 修改密码
func changePassword(x *vigo.X, req *ChangePasswordRequest) error {
userID := baseAuth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return vigo.ErrUnauthorized
}

@ -13,7 +13,6 @@ import (
"strings"
"time"
baseauth "github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/crypto"
"github.com/veypi/vbase/libs/jwt"
@ -151,7 +150,7 @@ func register(x *vigo.X, req *RegisterRequest) (*AuthResponse, error) {
roleCode = "admin"
}
if err := baseauth.VBaseAuth.GrantRole(x.Context(), user.ID, roleCode); err != nil {
if err := cfg.Auth.GrantRole(x.Context(), user.ID, roleCode); err != nil {
// 记录错误但允许注册继续,或者回滚
// 这里简单处理,继续流程,用户可能需要管理员手动授权
// 或者返回错误

@ -16,7 +16,6 @@ import (
"strings"
"time"
baseauth "github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/cache"
"github.com/veypi/vbase/libs/crypto"
@ -83,7 +82,7 @@ func authorizeThirdParty(x *vigo.X, req *AuthorizeRequest) (*AuthorizeResponse,
// 如果是绑定模式,需要当前用户登录
if req.BindMode != nil && *req.BindMode {
userID := baseauth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return nil, vigo.ErrUnauthorized.WithString("login required for bind mode")
}
@ -301,7 +300,7 @@ func bindWithRegister(x *vigo.X, req *BindWithRegisterRequest) (*AuthResponse, e
}
// 授予默认角色 "user"
if err := baseauth.VBaseAuth.GrantRole(x.Context(), user.ID, "user"); err != nil {
if err := cfg.Auth.GrantRole(x.Context(), user.ID, "user"); err != nil {
// 记录错误但允许流程继续
}
@ -321,7 +320,7 @@ type UnbindRequest struct {
// unbindThirdParty 解除第三方账号绑定
func unbindThirdParty(x *vigo.X, req *UnbindRequest) error {
userID := baseauth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return vigo.ErrUnauthorized
}
@ -345,7 +344,7 @@ type BindingInfo struct {
// listBindings 获取当前用户的第三方绑定列表
func listBindings(x *vigo.X) ([]BindingInfo, error) {
userID := baseauth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return nil, vigo.ErrUnauthorized
}

@ -13,7 +13,6 @@ import (
"github.com/veypi/vbase/api/settings"
"github.com/veypi/vbase/api/user"
"github.com/veypi/vbase/api/verification"
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
@ -47,12 +46,12 @@ type OAuthProviderInfo struct {
func init() {
// 注册全局中间件
Router.Use(auth.VBaseAuth.Login())
Router.Use(cfg.Auth.Login())
Router.After(common.JsonResponse, common.JsonErrorResponse)
// 初始化角色
auth.VBaseAuth.AddRole("admin", "管理员", "*:7")
auth.VBaseAuth.AddRole("user", "普通用户")
cfg.Auth.AddRole("admin", "管理员", "*:7")
cfg.Auth.AddRole("user", "普通用户")
// 子路由挂载
Router.Extend("/auth", apiAuth.Router)

@ -7,7 +7,6 @@ package oauth
import (
"time"
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/cache"
"github.com/veypi/vbase/libs/crypto"
@ -40,7 +39,7 @@ func authorize(x *vigo.X, req *AuthorizeRequest) (*AuthorizeResponse, error) {
}
// 获取当前用户
userID := auth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return nil, vigo.ErrUnauthorized
}

@ -5,7 +5,6 @@
package oauth
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/crypto"
"github.com/veypi/vbase/models"
@ -66,7 +65,7 @@ type CreateClientResponse struct {
}
func createClient(x *vigo.X, req *CreateClientRequest) (*CreateClientResponse, error) {
ownerID := auth.VBaseAuth.UserID(x)
ownerID := cfg.Auth.UserID(x)
if ownerID == "" {
return nil, vigo.ErrUnauthorized
}
@ -123,7 +122,7 @@ func updateClient(x *vigo.X, req *UpdateClientRequest) (*models.OAuthClient, err
}
// 检查权限:只有所有者或管理员可以修改
currentUserID := auth.VBaseAuth.UserID(x)
currentUserID := cfg.Auth.UserID(x)
if currentUserID == "" {
return nil, vigo.ErrUnauthorized
}
@ -132,7 +131,7 @@ func updateClient(x *vigo.X, req *UpdateClientRequest) (*models.OAuthClient, err
isOwner := client.OwnerID == currentUserID
// 检查是否是管理员(拥有 *:* 权限)
isAdmin := auth.VBaseAuth.Check(x.Context(), currentUserID, "*:*", auth.LevelAdmin)
isAdmin := cfg.Auth.Check(x.Context(), currentUserID, "*:*", 7)
if !isOwner && !isAdmin {
return nil, vigo.ErrForbidden.WithString("not the owner of this client")
@ -170,7 +169,7 @@ func deleteClient(x *vigo.X, req *DeleteClientRequest) error {
}
// 检查权限:只有所有者或管理员可以删除
currentUserID := auth.VBaseAuth.UserID(x)
currentUserID := cfg.Auth.UserID(x)
if currentUserID == "" {
return vigo.ErrUnauthorized
}
@ -179,7 +178,7 @@ func deleteClient(x *vigo.X, req *DeleteClientRequest) error {
isOwner := client.OwnerID == currentUserID
// 检查是否是管理员(拥有 *:* 权限)
isAdmin := auth.VBaseAuth.Check(x.Context(), currentUserID, "*:*", auth.LevelAdmin)
isAdmin := cfg.Auth.Check(x.Context(), currentUserID, "*:*", 7)
if !isOwner && !isAdmin {
return vigo.ErrForbidden.WithString("not the owner of this client")

@ -6,7 +6,7 @@ package oauth
import (
"github.com/veypi/vbase/api/oauth/providers"
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vigo"
)
@ -22,11 +22,11 @@ func init() {
// === OAuth 客户端管理(需要认证)===
clientRouter := Router.SubRouter("/clients")
clientRouter.Get("/", "OAuth客户端列表", auth.VBaseAuth.PermRead("oauth-client:*"), listClients)
clientRouter.Post("/", "创建OAuth客户端", auth.VBaseAuth.PermCreate("oauth-client"), createClient)
clientRouter.Get("/{client_id}", "获取客户端详情", auth.VBaseAuth.PermRead("oauth-client:*"), getClient)
clientRouter.Patch("/{client_id}", "更新OAuth客户端", auth.VBaseAuth.PermWrite("oauth-client:*"), updateClient)
clientRouter.Delete("/{client_id}", "删除OAuth客户端", auth.VBaseAuth.PermWrite("oauth-client:*"), deleteClient)
clientRouter.Get("/", "OAuth客户端列表", cfg.Auth.RequireRead("oauth-client:*"), listClients)
clientRouter.Post("/", "创建OAuth客户端", cfg.Auth.RequireCreate("oauth-client"), createClient)
clientRouter.Get("/{client_id}", "获取客户端详情", cfg.Auth.RequireRead("oauth-client:*"), getClient)
clientRouter.Patch("/{client_id}", "更新OAuth客户端", cfg.Auth.RequireWrite("oauth-client:*"), updateClient)
clientRouter.Delete("/{client_id}", "删除OAuth客户端", cfg.Auth.RequireWrite("oauth-client:*"), deleteClient)
// === OAuth 提供商管理 ===
Router.Extend("/providers", providers.Router)

@ -5,7 +5,6 @@
package oauth
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
@ -14,7 +13,7 @@ import (
// UserInfo OIDC用户信息
func userInfo(x *vigo.X) (map[string]any, error) {
// 从token中解析用户ID
userID := auth.VBaseAuth.UserID(x)
userID := cfg.Auth.UserID(x)
if userID == "" {
return nil, vigo.ErrUnauthorized
}

@ -7,7 +7,7 @@
package providers
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vigo"
)
@ -15,15 +15,15 @@ var Router = vigo.NewRouter()
func init() {
// 获取所有提供商(包括禁用的,管理员用)
Router.Get("/", "获取 OAuth 提供商列表", auth.VBaseAuth.PermRead("oauth-provider:*"), list)
Router.Get("/", "获取 OAuth 提供商列表", cfg.Auth.RequireRead("oauth-provider:*"), list)
// 获取单个提供商详情
Router.Get("/{code}", "获取 OAuth 提供商详情", auth.VBaseAuth.PermRead("oauth-provider:*"), get)
Router.Get("/{code}", "获取 OAuth 提供商详情", cfg.Auth.RequireRead("oauth-provider:*"), get)
// 创建新提供商
Router.Post("/", "创建 OAuth 提供商", auth.VBaseAuth.PermCreate("oauth-provider"), create)
Router.Post("/", "创建 OAuth 提供商", cfg.Auth.RequireCreate("oauth-provider"), create)
// 更新提供商
Router.Patch("/{code}", "更新 OAuth 提供商", auth.VBaseAuth.PermWrite("oauth-provider:*"), update)
Router.Patch("/{code}", "更新 OAuth 提供商", cfg.Auth.RequireWrite("oauth-provider:*"), update)
// 删除提供商(仅非内置)
Router.Delete("/{code}", "删除 OAuth 提供商", auth.VBaseAuth.PermWrite("oauth-provider:*"), del)
Router.Delete("/{code}", "删除 OAuth 提供商", cfg.Auth.RequireWrite("oauth-provider:*"), del)
// 获取内置模板
Router.Get("/templates", "获取内置 OAuth 模板", auth.VBaseAuth.PermRead("oauth-provider:*"), templates)
Router.Get("/templates", "获取内置 OAuth 模板", cfg.Auth.RequireRead("oauth-provider:*"), templates)
}

@ -1,18 +1,18 @@
package role
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vigo"
)
var Router = vigo.NewRouter()
func init() {
Router.Get("/", "List Roles", auth.VBaseAuth.PermRead("role:*"), list)
Router.Get("/{id}", "Get Role Detail", auth.VBaseAuth.PermRead("role:*"), get)
Router.Post("/", "Create Role", auth.VBaseAuth.PermCreate("role"), create)
Router.Patch("/{id}", "Update Role", auth.VBaseAuth.PermWrite("role:*"), patch)
Router.Delete("/{id}", "Delete Role", auth.VBaseAuth.PermWrite("role:*"), del)
Router.Get("/{id}/permissions", "Get Role Permissions", auth.VBaseAuth.PermRead("role:*"), getPermissions)
Router.Put("/{id}/permissions", "Update Role Permissions", auth.VBaseAuth.PermWrite("role:*"), updatePermissions)
Router.Get("/", "List Roles", cfg.Auth.RequireRead("role:*"), list)
Router.Get("/{id}", "Get Role Detail", cfg.Auth.RequireRead("role:*"), get)
Router.Post("/", "Create Role", cfg.Auth.RequireCreate("role"), create)
Router.Patch("/{id}", "Update Role", cfg.Auth.RequireWrite("role:*"), patch)
Router.Delete("/{id}", "Delete Role", cfg.Auth.RequireWrite("role:*"), del)
Router.Get("/{id}/permissions", "Get Role Permissions", cfg.Auth.RequireRead("role:*"), getPermissions)
Router.Put("/{id}/permissions", "Update Role Permissions", cfg.Auth.RequireWrite("role:*"), updatePermissions)
}

@ -7,7 +7,7 @@
package settings
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vigo"
)
@ -16,7 +16,7 @@ var Router = vigo.NewRouter()
func init() {
// 获取设置列表(支持按分类过滤)
// 只有拥有所有权限的可以修改配置表
Router.Get("/", "获取设置列表", auth.VBaseAuth.PermRead("setting:*"), list)
Router.Get("/", "获取设置列表", cfg.Auth.RequireRead("setting:*"), list)
// 批量更新设置
Router.Put("/", "批量更新设置", auth.VBaseAuth.PermWrite("setting:*"), update)
Router.Put("/", "批量更新设置", cfg.Auth.RequireWrite("setting:*"), update)
}

@ -7,7 +7,6 @@
package user
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/crypto"
"github.com/veypi/vbase/models"
@ -75,7 +74,7 @@ func create(x *vigo.X, req *CreateRequest) (*models.User, error) {
}
// 授予默认角色 "user"
if err := auth.VBaseAuth.GrantRole(x.Context(), user.ID, "user"); err != nil {
if err := cfg.Auth.GrantRole(x.Context(), user.ID, "user"); err != nil {
// 记录错误但允许流程继续
}

@ -7,7 +7,6 @@
package user
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
@ -21,9 +20,9 @@ type GetRequest struct {
// get 获取用户详情
func get(x *vigo.X, req *GetRequest) (*models.User, error) {
// 手动鉴权: 只能查看自己的信息,或者是管理员
uid := auth.VBaseAuth.UserID(x)
uid := cfg.Auth.UserID(x)
if uid != req.UserID {
if !auth.VBaseAuth.Check(x.Context(), uid, "user:read", auth.LevelRead) {
if !cfg.Auth.Check(x.Context(), uid, "user:read", 2) {
return nil, vigo.ErrForbidden
}
}

@ -7,7 +7,7 @@
package user
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vigo"
)
@ -15,15 +15,15 @@ var Router = vigo.NewRouter()
func init() {
// 管理员 管理用户权限 (所有接口需要 user:admin 权限)
Router.Get("/", "用户列表", auth.VBaseAuth.PermRead("user:*"), list)
Router.Post("/", "创建用户", auth.VBaseAuth.PermCreate("user"), create)
Router.Get("/{user_id}", "获取用户详情", auth.VBaseAuth.PermRead("user:*"), get)
Router.Patch("/{user_id}", "更新用户", auth.VBaseAuth.PermWrite("user:*"), patch)
Router.Delete("/{user_id}", "删除用户", auth.VBaseAuth.PermWrite("user:*"), del)
Router.Patch("/{user_id}/status", "更新用户状态", auth.VBaseAuth.PermWrite("user:*"), updateStatus)
Router.Get("/", "用户列表", cfg.Auth.RequireRead("user:*"), list)
Router.Post("/", "创建用户", cfg.Auth.RequireCreate("user"), create)
Router.Get("/{user_id}", "获取用户详情", cfg.Auth.RequireRead("user:*"), get)
Router.Patch("/{user_id}", "更新用户", cfg.Auth.RequireWrite("user:*"), patch)
Router.Delete("/{user_id}", "删除用户", cfg.Auth.RequireWrite("user:*"), del)
Router.Patch("/{user_id}/status", "更新用户状态", cfg.Auth.RequireWrite("user:*"), updateStatus)
Router.Get("/{user_id}/roles", "Get User Roles", auth.VBaseAuth.PermRead("user:*"), getRoles)
Router.Put("/{user_id}/roles", "Update User Roles", auth.VBaseAuth.PermWrite("user:*"), updateRoles)
Router.Get("/{user_id}/permissions", "Get User Permissions", auth.VBaseAuth.PermRead("user:*"), getPermissions)
Router.Put("/{user_id}/permissions", "Update User Permissions", auth.VBaseAuth.PermWrite("user:*"), updatePermissions)
Router.Get("/{user_id}/roles", "Get User Roles", cfg.Auth.RequireRead("user:*"), getRoles)
Router.Put("/{user_id}/roles", "Update User Roles", cfg.Auth.RequireWrite("user:*"), updateRoles)
Router.Get("/{user_id}/permissions", "Get User Permissions", cfg.Auth.RequireRead("user:*"), getPermissions)
Router.Put("/{user_id}/permissions", "Update User Permissions", cfg.Auth.RequireWrite("user:*"), updatePermissions)
}

@ -7,7 +7,6 @@
package user
import (
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
@ -25,9 +24,9 @@ type PatchRequest struct {
// patch 更新用户
func patch(x *vigo.X, req *PatchRequest) (*models.User, error) {
// 手动鉴权: 只能修改自己的信息,或者是管理员
uid := auth.VBaseAuth.UserID(x)
uid := cfg.Auth.UserID(x)
if uid != req.UserID {
if !auth.VBaseAuth.Check(x.Context(), uid, "user:update", auth.LevelWrite) {
if !cfg.Auth.Check(x.Context(), uid, "user:update", 4) {
return nil, vigo.ErrForbidden
}
}

@ -16,6 +16,7 @@ import (
"github.com/veypi/vbase/libs/sms"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
"github.com/veypi/vigo/contrib/requestmeta"
)
// SendRequest 发送验证码请求
@ -124,7 +125,7 @@ func sendCode(x *vigo.X, req *SendRequest) (*SendResponse, error) {
Purpose: req.Purpose,
Status: models.CodeStatusPending,
ExpiresAt: expiresAt,
RemoteIP: x.GetRemoteIP(),
RemoteIP: requestmeta.RemoteIP(x),
}
if err := db.Create(verification).Error; err != nil {
@ -216,4 +217,3 @@ func generateCode(length int) string {
}
return string(code)
}

@ -13,8 +13,6 @@ import (
"strings"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/libs/cache"
"github.com/veypi/vbase/libs/jwt"
"github.com/veypi/vbase/models"
"github.com/veypi/vigo"
pub "github.com/veypi/vigo/contrib/auth"
@ -38,15 +36,18 @@ const (
// PermFunc 权限检查函数类型
type PermFunc = pub.PermFunc
// Provider 是 auth.Provider 的别名,用于实现端
type Provider = pub.Provider
// Factory 全局 Auth 工厂
var Factory = &authFactory{
apps: make(map[string]*appAuth),
apps: make(map[string]Provider),
}
// VBaseAuth vbase 自身的权限管理实例
var VBaseAuth = Factory.New("vb")
// VBaseProvider vbase 自身的权限 Provider 实例
var VBaseProvider Provider
var _ pub.Auth = &appAuth{}
var _ pub.Provider = &vbaseProvider{}
func init() {
// 注册权限初始化回调
@ -54,26 +55,28 @@ func init() {
}
type authFactory struct {
apps map[string]*appAuth
apps map[string]Provider
}
// New 创建权限管理实例
func (f *authFactory) New(scope string) pub.Auth {
if auth, exists := f.apps[scope]; exists {
return auth
// New 创建权限 Provider 实例
func (f *authFactory) New(scope string) Provider {
if p, exists := f.apps[scope]; exists {
return p
}
auth := &appAuth{
p := &vbaseProvider{
scope: scope,
roleDefs: make(map[string]roleDefinition),
}
f.apps[scope] = auth
return auth
f.apps[scope] = p
return p
}
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)
for appKey, p := range f.apps {
if vp, ok := p.(*vbaseProvider); ok {
if err := vp.init(); err != nil {
return fmt.Errorf("failed to init auth for %s: %w", appKey, err)
}
}
}
return nil
@ -86,113 +89,23 @@ type roleDefinition struct {
policies []string // 格式: "permissionID:level"
}
// appAuth 实现 Auth 接口
type appAuth struct {
// vbaseProvider 实现 Provider 接口
type vbaseProvider struct {
scope string
roleDefs map[string]roleDefinition
}
// ========== 接口实现 ==========
// ========== Provider 接口实现 ==========
func (a *appAuth) UserID(x *vigo.X) string {
func (a *vbaseProvider) UserID(x *vigo.X) string {
if uid, ok := x.Get(CtxKeyUserID).(string); ok {
return uid
}
return ""
}
// Login 登录检查中间件
func (a *appAuth) Login() PermFunc {
return func(x *vigo.X) error {
// 1. 提取 token
tokenString := extractToken(x)
if tokenString == "" {
return vigo.ErrUnauthorized.WithString("missing token")
}
// 2. 解析 token
claims, err := jwt.ParseToken(tokenString)
if err != nil {
if err == jwt.ErrExpiredToken {
return vigo.ErrTokenExpired
}
return vigo.ErrTokenInvalid
}
// 3. 检查黑名单
if cache.IsEnabled() {
blacklisted, _ := cache.IsTokenBlacklisted(claims.ID)
if blacklisted {
return vigo.ErrUnauthorized.WithString("token has been revoked")
}
}
// 4. 设置 UserID
x.Set(CtxKeyUserID, claims.UserID)
return nil
}
}
func (a *appAuth) Perm(code string, level int) PermFunc {
return func(x *vigo.X) error {
userID := a.UserID(x)
if userID == "" {
// 尝试先运行 Login 逻辑
if err := a.Login()(x); err != nil {
return err
}
userID = a.UserID(x)
}
// 解析动态参数
permID, err := parsePermissionID(x, code)
if err != nil {
return vigo.ErrInvalidArg.WithError(err)
}
// 检查权限
if err := validatePermission(permID, level); err != nil {
panic(err)
}
if !a.Check(x.Context(), userID, permID, level) {
return vigo.ErrNoPermission.WithString(fmt.Sprintf("requires permission: %s (level %d)", permID, level))
}
return nil
}
}
func (a *appAuth) PermCreate(code string) PermFunc {
if err := validatePermission(code, LevelCreate); err != nil {
panic(err)
}
return a.Perm(code, LevelCreate)
}
func (a *appAuth) PermRead(code string) PermFunc {
if err := validatePermission(code, LevelRead); err != nil {
panic(err)
}
return a.Perm(code, LevelRead)
}
func (a *appAuth) PermWrite(code string) PermFunc {
if err := validatePermission(code, LevelWrite); err != nil {
panic(err)
}
return a.Perm(code, LevelWrite)
}
func (a *appAuth) PermAdmin(code string) PermFunc {
if err := validatePermission(code, LevelAdmin); err != nil {
panic(err)
}
return a.Perm(code, LevelAdmin)
}
// Grant 授予权限
func (a *appAuth) Grant(ctx context.Context, userID, permissionID string, level int) error {
func (a *vbaseProvider) Grant(ctx context.Context, userID, permissionID string, level int) error {
if err := validatePermission(permissionID, level); err != nil {
return err
}
@ -220,13 +133,13 @@ func (a *appAuth) Grant(ctx context.Context, userID, permissionID string, level
}
// Revoke 撤销权限
func (a *appAuth) Revoke(ctx context.Context, userID, permissionID string) error {
func (a *vbaseProvider) Revoke(ctx context.Context, userID, permissionID string) error {
return cfg.DB().Where("user_id = ? AND permission_id = ? AND scope = ?", userID, permissionID, a.scope).
Delete(&models.Permission{}).Error
}
// GrantRole 授予角色
func (a *appAuth) GrantRole(ctx context.Context, userID, roleCode string) error {
func (a *vbaseProvider) GrantRole(ctx context.Context, userID, roleCode string) error {
var role models.Role
if err := cfg.DB().Where("code = ?", roleCode).First(&role).Error; err != nil {
return err
@ -248,7 +161,7 @@ func (a *appAuth) GrantRole(ctx context.Context, userID, roleCode string) error
}
// RevokeRole 撤销角色
func (a *appAuth) RevokeRole(ctx context.Context, userID, roleCode string) error {
func (a *vbaseProvider) RevokeRole(ctx context.Context, userID, roleCode string) error {
var role models.Role
if err := cfg.DB().Where("code = ?", roleCode).First(&role).Error; err != nil {
return err
@ -259,7 +172,7 @@ func (a *appAuth) RevokeRole(ctx context.Context, userID, roleCode string) error
}
// Check 检查权限
func (a *appAuth) Check(ctx context.Context, userID, permissionID string, level int) bool {
func (a *vbaseProvider) Check(ctx context.Context, userID, permissionID string, level int) bool {
if err := validatePermission(permissionID, level); err != nil {
panic(err)
}
@ -274,7 +187,7 @@ func (a *appAuth) Check(ctx context.Context, userID, permissionID string, level
}
// ListResources 查询用户在特定资源类型下的详细权限信息
func (a *appAuth) ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error) {
func (a *vbaseProvider) ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error) {
perms, err := a.getUserPermissions(userID)
if err != nil {
return nil, err
@ -309,7 +222,7 @@ func (a *appAuth) ListResources(ctx context.Context, userID, resourceType string
}
// ListUsers 查询特定资源的所有协作者及其权限
func (a *appAuth) ListUsers(ctx context.Context, permissionID string) (map[string]int, error) {
func (a *vbaseProvider) ListUsers(ctx context.Context, permissionID string) (map[string]int, error) {
parents := getAllParents(permissionID)
var perms []models.Permission
@ -365,7 +278,7 @@ func getAllParents(permID string) []string {
}
// AddRole 添加角色定义
func (a *appAuth) AddRole(code, name string, policies ...string) error {
func (a *vbaseProvider) AddRole(code, name string, policies ...string) error {
a.roleDefs[code] = roleDefinition{
code: code,
name: name,
@ -375,7 +288,7 @@ func (a *appAuth) AddRole(code, name string, policies ...string) error {
}
// init 初始化角色到数据库
func (a *appAuth) init() error {
func (a *vbaseProvider) init() error {
db := cfg.DB()
for code, def := range a.roleDefs {
// 1. 确保角色存在
@ -462,7 +375,7 @@ func (a *appAuth) init() error {
// ========== 内部辅助方法 ==========
// getUserPermissions 获取用户的所有权限(聚合 Role 和 Direct Permission
func (a *appAuth) getUserPermissions(userID string) ([]models.Permission, error) {
func (a *vbaseProvider) getUserPermissions(userID string) ([]models.Permission, error) {
var perms []models.Permission
db := cfg.DB()

@ -13,6 +13,7 @@ package cfg
import (
"time"
"github.com/veypi/vigo/contrib/auth"
"github.com/veypi/vigo/contrib/config"
)
@ -78,3 +79,6 @@ var Global = &Options{
var DB = Global.DB.Client
var Redis = Global.Redis.Client
// Auth 是全局的权限检查对象,通过 SetProvider 注入 Provider 实现
var Auth = auth.New()

@ -18,12 +18,25 @@ import (
"github.com/veypi/vigo"
)
const Version = "v0.6.4"
var Router = vigo.NewRouter()
var (
NewAuth = auth.Factory.New
Config = cfg.Global
)
func init() {
// 初始化 VBaseProvider 并设置到全局 Auth
auth.VBaseProvider = auth.Factory.New("vb")
cfg.Auth.SetProvider(auth.VBaseProvider)
Router.Extend("/api", api.Router)
Router.Extend("v", vhtmlui.Router)
Router.Extend("vhtml", vhtml.Router)
vhtml.WrapUI(Router, uifs)
}
type Options = cfg.Options
func Init() error {

@ -5,6 +5,7 @@ import (
"testing"
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
)
// OAuthClientResp OAuth 客户端响应
@ -106,10 +107,10 @@ func TestOAuthClientAccessControl(t *testing.T) {
ensureUsers(t)
// Grant permission to User1
if err := auth.VBaseAuth.Grant(context.Background(), User1ID, "oauth-client:*", auth.LevelRead); err != nil {
if err := cfg.Auth.Grant(context.Background(), User1ID, "oauth-client:*", auth.LevelRead); err != nil {
t.Fatalf("Failed to grant read permission: %v", err)
}
if err := auth.VBaseAuth.Grant(context.Background(), User1ID, "oauth-client", auth.LevelCreate); err != nil {
if err := cfg.Auth.Grant(context.Background(), User1ID, "oauth-client", auth.LevelCreate); err != nil {
t.Fatalf("Failed to grant create permission: %v", err)
}

@ -1,9 +1,11 @@
package tests
import (
"testing"
"context"
"testing"
"github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg"
)
// TestOAuthClientSecretLeak 测试 OAuth 客户端密钥泄漏
@ -11,7 +13,7 @@ func TestOAuthClientSecretLeak(t *testing.T) {
ensureUsers(t)
// Grant permission to User1 to see client details
auth.VBaseAuth.Grant(context.Background(), User1ID, "oauth-client:*", auth.LevelRead)
cfg.Auth.Grant(context.Background(), User1ID, "oauth-client:*", auth.LevelRead)
var clientID string
var clientSecret string
@ -81,7 +83,7 @@ func TestOAuthClientAccessControlSecurity(t *testing.T) {
ensureUsers(t)
// Grant permission to User1 to create clients
auth.VBaseAuth.Grant(context.Background(), User1ID, "oauth-client", auth.LevelCreate)
cfg.Auth.Grant(context.Background(), User1ID, "oauth-client", auth.LevelCreate)
var clientID string

@ -17,9 +17,9 @@ func TestRoleApiAccess(t *testing.T) {
// Ensure Admin has * permission
// Clean up any previous permissions for Admin
cfg.DB().Where("user_id = ?", AdminID).Delete(&models.Permission{})
// Grant Admin * permission
if err := auth.VBaseAuth.Grant(ctx, AdminID, "*", auth.LevelAdmin); err != nil {
if err := cfg.Auth.Grant(ctx, AdminID, "*", auth.LevelAdmin); err != nil {
t.Fatalf("Failed to grant admin permission: %v", err)
}
@ -44,7 +44,7 @@ func TestRoleApiAccess(t *testing.T) {
// 3. User Access (With Permission)
t.Run("User_WithPermission_Role_List", func(t *testing.T) {
// Grant role:* (Read) to User1
if err := auth.VBaseAuth.Grant(ctx, User1ID, "role:*", auth.LevelRead); err != nil {
if err := cfg.Auth.Grant(ctx, User1ID, "role:*", auth.LevelRead); err != nil {
t.Fatalf("Failed to grant role permission: %v", err)
}

@ -30,8 +30,8 @@ func TestScopedAuth(t *testing.T) {
t.Errorf("Expected User1 to have %s in %s scope", permID, scope)
}
// Verify Check in global VBaseAuth scope (should be false)
if auth.VBaseAuth.Check(ctx, User1ID, permID, level) {
// Verify Check in global VBaseProvider scope (should be false)
if auth.VBaseProvider.Check(ctx, User1ID, permID, level) {
t.Errorf("Expected User1 NOT to have %s in 'vb' scope", permID)
}
})

Loading…
Cancel
Save