mirror of https://github.com/veypi/OneAuth.git
update
parent
8fa01c4c52
commit
26bd0bfd29
@ -1,155 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2024 veypi <i@veypi.com>
|
||||
// 2025-03-04 16:08:06
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/veypi/vbase/cfg"
|
||||
"github.com/veypi/vbase/models"
|
||||
"github.com/veypi/vigo"
|
||||
)
|
||||
|
||||
// Checker 权限检查器
|
||||
type Checker struct {
|
||||
userID string
|
||||
orgID string
|
||||
roles []string
|
||||
org *models.Org
|
||||
}
|
||||
|
||||
// NewChecker 创建权限检查器
|
||||
func NewChecker(x *vigo.X) *Checker {
|
||||
c := &Checker{}
|
||||
|
||||
if uid, ok := x.Get("user_id").(string); ok {
|
||||
c.userID = uid
|
||||
}
|
||||
if oid, ok := x.Get("org_id").(string); ok {
|
||||
c.orgID = oid
|
||||
}
|
||||
if roles, ok := x.Get("org_roles").(string); ok && roles != "" {
|
||||
c.roles = strings.Split(roles, ",")
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// IsOrgOwner 检查用户是否是组织所有者
|
||||
func (c *Checker) IsOrgOwner() bool {
|
||||
if c.orgID == "" || c.userID == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.org != nil {
|
||||
return c.org.OwnerID == c.userID
|
||||
}
|
||||
|
||||
var org models.Org
|
||||
if err := cfg.DB().First(&org, "id = ?", c.orgID).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
c.org = &org
|
||||
|
||||
return org.OwnerID == c.userID
|
||||
}
|
||||
|
||||
// IsOrgAdmin 检查用户是否是组织管理员
|
||||
func (c *Checker) IsOrgAdmin() bool {
|
||||
if c.IsOrgOwner() {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, roleID := range c.roles {
|
||||
var role models.Role
|
||||
if err := cfg.DB().First(&role, "id = ?", roleID).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
if role.Code == models.RoleCodeAdmin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasRole 检查用户是否有指定角色
|
||||
func (c *Checker) HasRole(roleCode string) bool {
|
||||
for _, roleID := range c.roles {
|
||||
var role models.Role
|
||||
if err := cfg.DB().First(&role, "id = ?", roleID).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
if role.Code == roleCode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RequireAdmin 要求管理员权限
|
||||
func (c *Checker) RequireAdmin() error {
|
||||
if !c.IsOrgAdmin() {
|
||||
return vigo.ErrForbidden.WithString("admin permission required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequireOwner 要求所有者权限
|
||||
func (c *Checker) RequireOwner() error {
|
||||
if !c.IsOrgOwner() {
|
||||
return vigo.ErrForbidden.WithString("owner permission required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequireOrg 要求必须在组织上下文中
|
||||
func (c *Checker) RequireOrg() error {
|
||||
if c.orgID == "" {
|
||||
return vigo.ErrArgInvalid.WithString("organization context required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UserID 获取用户ID
|
||||
func (c *Checker) UserID() string {
|
||||
return c.userID
|
||||
}
|
||||
|
||||
// OrgID 获取组织ID
|
||||
func (c *Checker) OrgID() string {
|
||||
return c.orgID
|
||||
}
|
||||
|
||||
// GetUserRoles 获取用户角色列表
|
||||
func (c *Checker) GetUserRoles() []string {
|
||||
return c.roles
|
||||
}
|
||||
|
||||
// RequireAdmin 中间件:要求管理员权限
|
||||
func RequireAdmin() func(*vigo.X) error {
|
||||
return func(x *vigo.X) error {
|
||||
checker := NewChecker(x)
|
||||
return checker.RequireAdmin()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireOwner 中间件:要求组织所有者权限
|
||||
func RequireOwner() func(*vigo.X) error {
|
||||
return func(x *vigo.X) error {
|
||||
checker := NewChecker(x)
|
||||
return checker.RequireOwner()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireOrgContext 中间件:要求必须在组织上下文中
|
||||
func RequireOrgContext() func(*vigo.X) error {
|
||||
return func(x *vigo.X) error {
|
||||
checker := NewChecker(x)
|
||||
return checker.RequireOrg()
|
||||
}
|
||||
}
|
||||
@ -1,157 +0,0 @@
|
||||
# VBase 集成指南
|
||||
|
||||
## 1. 引入路由
|
||||
|
||||
```go
|
||||
import "github.com/veypi/vbase/api"
|
||||
|
||||
func main() {
|
||||
// 挂载 vbase 路由到 /api/vb
|
||||
rootRouter.Extend("/api/vb", api.Router)
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 配置权限中间件
|
||||
|
||||
```go
|
||||
import "github.com/veypi/vbase/api/middleware"
|
||||
|
||||
// 全局启用路由权限检查(推荐)
|
||||
router.Use(middleware.Perm("your-domain"))
|
||||
|
||||
// 未配置的接口默认拒绝
|
||||
// 无权限时返回 X-Required-URL 响应头
|
||||
```
|
||||
|
||||
## 3. 注册路由权限
|
||||
|
||||
### 3.1 自动注册(推荐)
|
||||
|
||||
```go
|
||||
// 启动时自动扫描并注册所有路由
|
||||
middleware.InitVBaseRoutePolicies()
|
||||
|
||||
// 首次启动后,在数据库中修改权限配置即可
|
||||
```
|
||||
|
||||
### 3.2 手动注册
|
||||
|
||||
```go
|
||||
// 公开接口
|
||||
middleware.RegisterRoutePolicy("myapp", "/api", "/public", "GET", "public", "read", true)
|
||||
|
||||
// 需要权限的接口
|
||||
middleware.RegisterRoutePolicy("myapp", "/api", "/users", "GET", "user", "list", false)
|
||||
middleware.RegisterRoutePolicy("myapp", "/api", "/users/{id}", "PATCH", "user", "update", false)
|
||||
|
||||
// 重新加载缓存
|
||||
middleware.ReloadRoutePolicies()
|
||||
```
|
||||
|
||||
## 4. 数据库配置权限
|
||||
|
||||
### 4.1 路由权限表 (route_policies)
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| domain | 领域标识 | "myapp" |
|
||||
| prefix | 路由前缀 | "/api" |
|
||||
| pattern | 路由模式 | "/users/{id}" |
|
||||
| method | HTTP方法 | "GET" |
|
||||
| resource | 资源类型 | "user" |
|
||||
| action | 操作类型 | "read" |
|
||||
| effect | 效果 | "allow"/"deny" |
|
||||
| condition | 条件 | "owner"/"admin" |
|
||||
| required_url | 申请地址 | "/apply-perm?r=user&a=read" |
|
||||
| is_public | 是否公开 | true/false |
|
||||
|
||||
### 4.2 用户权限表 (policies)
|
||||
|
||||
```sql
|
||||
-- 用户拥有 user:read 权限
|
||||
INSERT INTO policies (code, resource, action, effect)
|
||||
VALUES ('user-read', 'user', 'read', 'allow');
|
||||
|
||||
-- 关联到角色
|
||||
INSERT INTO roles (code, name, policy_ids)
|
||||
VALUES ('viewer', '查看者', 'policy-id-1,policy-id-2');
|
||||
```
|
||||
|
||||
## 5. 在 Handler 中设置资源所有者
|
||||
|
||||
```go
|
||||
func getUser(x *vigo.X, req *GetUserReq) (*User, error) {
|
||||
user := queryUser(req.ID)
|
||||
|
||||
// 设置所有者到上下文,用于 condition=owner 检查
|
||||
x.Set("owner_id", user.ID)
|
||||
|
||||
return user, nil
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 完整示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/veypi/vbase/api"
|
||||
"github.com/veypi/vbase/api/middleware"
|
||||
"github.com/veypi/vigo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := vigo.NewRouter()
|
||||
|
||||
// 1. 挂载 vbase
|
||||
r.Extend("/api/vb", api.Router)
|
||||
|
||||
// 2. 业务路由
|
||||
api := r.SubRouter("/api/v1")
|
||||
|
||||
// 3. 启用权限检查
|
||||
api.Use(middleware.AuthRequired())
|
||||
api.Use(middleware.OrgContext())
|
||||
api.Use(middleware.Perm("myapp"))
|
||||
|
||||
// 4. 注册权限(只需一次,后续在数据库中配置)
|
||||
middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects", "GET", "project", "list", false)
|
||||
middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects", "POST", "project", "create", false)
|
||||
middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects/{id}", "GET", "project", "read", false)
|
||||
middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects/{id}", "PATCH", "project", "update", false)
|
||||
middleware.RegisterRoutePolicy("myapp", "/api/v1", "/projects/{id}", "DELETE", "project", "delete", false)
|
||||
|
||||
// 5. 无需在 handler 中加权限代码
|
||||
api.Get("/projects", "项目列表", listProjects)
|
||||
api.Post("/projects", "创建项目", createProject)
|
||||
api.Get("/projects/{id}", "项目详情", getProject)
|
||||
api.Patch("/projects/{id}", "更新项目", updateProject)
|
||||
api.Delete("/projects/{id}", "删除项目", deleteProject)
|
||||
|
||||
vigo.Run(r)
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 权限检查流程
|
||||
|
||||
```
|
||||
请求 → AuthRequired(认证) → OrgContext(组织) → Perm(权限检查) → Handler
|
||||
↓
|
||||
查找 route_policies
|
||||
↓
|
||||
匹配 domain + method + path
|
||||
↓
|
||||
检查 resource:action 权限
|
||||
↓
|
||||
无权限 → 返回 X-Required-URL
|
||||
```
|
||||
|
||||
## 8. 配置热更新
|
||||
|
||||
```go
|
||||
// 修改数据库后,调用 API 刷新缓存
|
||||
middleware.ReloadRoutePolicies()
|
||||
```
|
||||
|
||||
无需重启服务,权限配置实时生效。
|
||||
@ -1,26 +0,0 @@
|
||||
# TSBD
|
||||
|
||||
```bash
|
||||
|
||||
docker run -dit --name=tsdb -v /Users/veypi/test/vdb:/victoria-metrics-data -p 8428:8428 victoriametrics/victoria-metrics -search.latencyOffset=1s -retentionPeriod=10y -search.cacheTimestampOffset=1h
|
||||
|
||||
|
||||
# 查询所有数据
|
||||
curl http://localhost:8428/prometheus/api/v1/series -d 'match[]={__name__=~".*"}'
|
||||
|
||||
# 删除所有数据
|
||||
curl http://localhost:8428/prometheus/api/v1/series\?start=1593100800\&end=1719308095\&step=2d22h40m50s -d 'match[]={__name__=~".*"}'
|
||||
curl http://localhost:8428/api/v1/admin/tsdb/delete_series -d 'match[]={__name__=~".*"}'
|
||||
|
||||
# 查询所有标签
|
||||
curl http://localhost:8428/prometheus/api/v1/labels
|
||||
|
||||
# 插入数据
|
||||
curl -X POST http://localhost:8428/api/v1/import/prometheus -d 'abd{foo="bar",a="15"} 25'
|
||||
curl -X POST http://localhost:8428/api/v1/import -d '{"metric":{"__name__":"foo","job":"node_exporter"},"values":[4],"timestamps":[1719819673000]}'
|
||||
|
||||
curl -G 'http://localhost:8428/api/v1/export' -d 'match={__name__="abd"}'
|
||||
curl -G 'http://localhost:8428/api/v1/export' -d 'match[]={__name__="abd"}'
|
||||
|
||||
|
||||
```
|
||||
Loading…
Reference in New Issue