refactor(bookmark): 重构 bookmark 服务生成配置和内部引用
将 bookmark 和 user_np 服务的生成配置分离为 server 和 client 包, 并更新了相应的导入路径。同时更新了 OpenAPI 配置文件中的包名、输出路径及 启用 strict-server 模式以增强类型安全。 此外,同步更新了 VFS 服务的客户端和服务端生成配置,并完善了其 OpenAPI 错误响应定义与二进制内容支持,增强了 API 的规范性和健壮性。 修复了部分权限校验逻辑,并调整了中间件注册方式以适配新的严格模式接口。
This commit is contained in:
@ -2,10 +2,9 @@ package vfs
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs"
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs_server"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -13,13 +12,153 @@ import (
|
||||
//go:embed vfs_model.conf
|
||||
var CasbinModel string
|
||||
|
||||
func NewVfsPermission() (*api.GinServerOptions, error) {
|
||||
return &api.GinServerOptions{
|
||||
Middlewares: []api.MiddlewareFunc{VfsMiddleware()},
|
||||
}, nil
|
||||
// 使用示例:在main.go或路由注册处
|
||||
func RegisterVFSRoutes(router gin.IRouter, vfsHandler *VfsImpl) {
|
||||
// 创建严格模式handler并添加权限中间件
|
||||
strictHandler := api.NewStrictHandler(vfsHandler, []api.StrictMiddlewareFunc{
|
||||
vfsHandler.PermissionMiddleware,
|
||||
})
|
||||
|
||||
// 注册handler
|
||||
api.RegisterHandlers(router, strictHandler)
|
||||
}
|
||||
|
||||
func (v *VfsImpl) CheckPermission(token, path, action string) (bool, error) {
|
||||
// PermissionMiddleware 是一个权限验证中间件
|
||||
func (v *VfsImpl) PermissionMiddleware(handler api.StrictHandlerFunc, operation string) api.StrictHandlerFunc {
|
||||
return func(ctx *gin.Context, request interface{}) (interface{}, error) {
|
||||
// 从请求头获取token
|
||||
token := ctx.GetHeader("X-VFS-Token")
|
||||
|
||||
// Admin token 拥有所有权限
|
||||
if token == v.config.AdminToken && len(token) != 0 {
|
||||
return handler(ctx, request)
|
||||
}
|
||||
|
||||
// 根据操作类型进行不同的权限检查
|
||||
switch operation {
|
||||
case "CreateUser":
|
||||
// 只有admin或register token可以创建用户
|
||||
if token != v.config.RegisterToken {
|
||||
return api.CreateUser403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return handler(ctx, request)
|
||||
|
||||
// 如果是删除用户操作,检查是否具有管理员权限
|
||||
case "DeleteUser":
|
||||
// 获取要删除的用户名
|
||||
var username string
|
||||
if req, ok := request.(api.DeleteUserRequestObject); ok {
|
||||
username = req.Username
|
||||
}
|
||||
|
||||
// 验证token对应的用户是否存在
|
||||
user, err := v.vfs.GetUserByToken(token)
|
||||
if err != nil || (user.Name != username && token != v.config.AdminToken) {
|
||||
return api.DeleteUser403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
// 如果验证通过,继续执行原处理函数
|
||||
return handler(ctx, request)
|
||||
case "GetVFSNode", "CreateVFSNode", "UpdateVFSNode", "DeleteVFSNode":
|
||||
// VFS节点操作的权限检查
|
||||
var path string
|
||||
var method string
|
||||
|
||||
// 根据操作类型获取路径和HTTP方法
|
||||
switch operation {
|
||||
case "GetVFSNode":
|
||||
if req, ok := request.(api.GetVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "GET"
|
||||
case "CreateVFSNode":
|
||||
if req, ok := request.(api.CreateVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "POST"
|
||||
case "UpdateVFSNode":
|
||||
if req, ok := request.(api.UpdateVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "PATCH"
|
||||
case "DeleteVFSNode":
|
||||
if req, ok := request.(api.DeleteVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "DELETE"
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
allowed, err := v.CheckPermission(token, path, method)
|
||||
if err != nil {
|
||||
return api.GetVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to check permission: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
// 根据操作类型返回相应的错误响应
|
||||
switch operation {
|
||||
case "GetVFSNode":
|
||||
return api.GetVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
case "CreateVFSNode":
|
||||
return api.CreateVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
case "UpdateVFSNode":
|
||||
return api.UpdateVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
case "DeleteVFSNode":
|
||||
return api.DeleteVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return handler(ctx, request)
|
||||
default:
|
||||
return api.Error{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "not supported function it's a bug in backend",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPermission 检查用户对指定路径的权限
|
||||
// FIXME: using casbin to check it
|
||||
func (v *VfsImpl) CheckPermission(token, path, method string) (bool, error) {
|
||||
// Admin token 拥有所有权限
|
||||
if token == v.config.AdminToken && len(token) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 根据 token 获取用户信息
|
||||
user, err := v.vfs.GetUserByToken(token)
|
||||
if err != nil {
|
||||
@ -27,13 +166,8 @@ func (v *VfsImpl) CheckPermission(token, path, action string) (bool, error) {
|
||||
user = &models.VfsUser{Name: "", Token: ""}
|
||||
}
|
||||
|
||||
// 特殊处理:admin 用户拥有所有权限
|
||||
if token == v.config.AdminToken && len(token) != 0 { // admin 用户拥有所有权限
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 允许任何人读取 public 目录
|
||||
if strings.HasPrefix(path, "/public") && action == "GET" {
|
||||
if method == "GET" && strings.HasPrefix(path, "/public") {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@ -42,48 +176,14 @@ func (v *VfsImpl) CheckPermission(token, path, action string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 构造 Casbin 请求
|
||||
// 对于普通用户,需要将策略中的 {{username}} 替换为实际用户名
|
||||
obj := path
|
||||
sub := user.Name
|
||||
// 使用 Casbin 检查权限
|
||||
allowed, err := v.enfocer.Enforce(sub, obj, action)
|
||||
// sub: 用户名 (匿名用户为空字符串)
|
||||
// obj: 路径
|
||||
// act: HTTP方法
|
||||
allowed, err := v.enfocer.Enforce(user.Name, path, method)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
func (v *VfsImpl) CheckPermissionMiddleware(c *gin.Context, path string) bool {
|
||||
token := c.GetHeader("X-VFS-Token")
|
||||
allowed, err := v.CheckPermission(token, path, c.Request.Method)
|
||||
// log.Println("CheckPermission:", allowed, err)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "PermissionCheckError",
|
||||
Message: "Failed to check permission: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
if !allowed {
|
||||
c.JSON(http.StatusForbidden, api.Error{
|
||||
Errtype: "AccessDenied",
|
||||
Message: "Access denied",
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func VfsMiddleware() api.MiddlewareFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 检查当前请求是否需要认证
|
||||
if _, exists := c.Get(api.ApiKeyAuthScopes); exists {
|
||||
// 提取 API Key
|
||||
apiKey := c.GetHeader("X-VFS-Token")
|
||||
c.Set(api.ApiKeyAuthScopes, apiKey)
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user