refactor(bookmark): 重构 bookmark 服务生成配置和内部引用

将 bookmark 和 user_np 服务的生成配置分离为 server 和 client 包,
并更新了相应的导入路径。同时更新了 OpenAPI 配置文件中的包名、输出路径及
启用 strict-server 模式以增强类型安全。

此外,同步更新了 VFS 服务的客户端和服务端生成配置,并完善了其 OpenAPI
错误响应定义与二进制内容支持,增强了 API 的规范性和健壮性。

修复了部分权限校验逻辑,并调整了中间件注册方式以适配新的严格模式接口。
This commit is contained in:
zzy
2025-09-25 16:15:34 +08:00
parent 24f238f377
commit b2cc27b8f5
18 changed files with 878 additions and 488 deletions

View File

@ -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()
}
}