30d667954b
统一强制、别靠人肉:受租户模型标记 isTenantScoped() 后,store/tenant_scope.go 的 gorm 回调按请求 ctx 自动给查询加 WHERE tenant_id、创建自动填 tenant_id。 - KB / Agent 加 TenantID 字段 + isTenantScoped() 标记 - middleware.TenantContext 把 tenant 注入 request context 供 store 插件读取 - ctx 无租户(系统/回填/未登录)不过滤,保留跨租户操作能力 - 启动 BackfillRowTenants 回填存量行 tenant_id=owner 默认租户(幂等) live 验证:创建自动写 tenant_id ✓;同 owner 不同 tenant 的行被查询过滤 ✓。 Doc/DocLink(异步入库)、Task/Eval(无 owner)留待增量2b。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.3 KiB
Go
32 lines
1.3 KiB
Go
package middleware
|
||
|
||
import (
|
||
"github.com/gin-gonic/gin"
|
||
|
||
"github.com/sundynix/sundynix-gateway/internal/store"
|
||
)
|
||
|
||
// CtxTenantID 是鉴权后写入 gin.Context 的当前租户 ID 键。
|
||
const CtxTenantID = "tenant_id"
|
||
|
||
// TenantContext 解析当前用户的默认租户并注入 tenant_id(多租户作用域的事实源)。
|
||
// 须挂在 Auth 之后(依赖已注入的 uid);未登录请求跳过。存量/异常无租户者由 EnsureDefaultTenant
|
||
// 幂等兜底补建,保证任何已登录请求都能拿到 tenant_id。
|
||
//
|
||
// 注:当前每请求解析一次(1–2 条按索引查询)。后续可把 tenant_id 嵌入 JWT / 加缓存去掉此开销。
|
||
func TenantContext(db *store.Postgres) gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
if v, ok := c.Get(CtxUserID); ok {
|
||
if uid, _ := v.(string); uid != "" {
|
||
if t, err := db.EnsureDefaultTenant(c.Request.Context(), uid, ""); err == nil && t != nil {
|
||
c.Set(CtxTenantID, t.ID)
|
||
// 注入请求 context:store 的 gorm 租户插件据此对受租户表自动加 tenant_id 过滤/填充。
|
||
// handler 须用 c.Request.Context() 调 store(现有代码已如此),隔离才会生效。
|
||
c.Request = c.Request.WithContext(store.WithTenant(c.Request.Context(), t.ID))
|
||
}
|
||
}
|
||
}
|
||
c.Next()
|
||
}
|
||
}
|