🚀 RAGFlow 知识库权限系统扩展设计

从简单二元权限到细粒度、可配置的企业级权限管理体系

📅 2026-06-10 📦 基于 RAGFlow 最新源码 🎯 版本: v2.0 设计草案 📋 状态: 设计评审

一、原始表结构分析

基于 api/db/db_models.py 中 Peewee ORM 模型提取的核心表结构。

1.1 核心实体关系概览

┌──────────────┐       ┌──────────────────┐       ┌──────────────┐
│    User      │       │   UserTenant     │       │   Tenant     │
├──────────────┤       ├──────────────────┤       ├──────────────┤
│ id (PK)      │──1:N──│ user_id (FK)     │──N:1──│ id (PK)      │
│ access_token │       │ tenant_id (FK)   │       │ name         │
│ nickname     │       │ role: owner|     │       │ llm_id       │
│ email        │       │   admin|normal|  │       │ embd_id      │
│ password     │       │   invite         │       │ credit       │
│ is_superuser │       │ invited_by       │       │ status       │
│ status       │       │ status           │       │ ...          │
└──────────────┘       └──────────────────┘       └──────┬───────┘
                                                          │
                     ┌────────────────────────────────────┤
                     │ 1:N                                │ 1:N
                     ▼                                    ▼
          ┌──────────────────┐              ┌──────────────────────┐
          │  Knowledgebase   │              │      Dialog          │
          ├──────────────────┤              ├──────────────────────┤
          │ id (PK)          │              │ id (PK)              │
          │ tenant_id (FK)   │              │ tenant_id (FK)       │
          │ name             │              │ name                 │
          │ permission:      │              │ kb_ids (JSON) ───────│── 引用多个 KB
          │   me | team      │◄─────────────│ llm_setting          │
          │ created_by       │              │ prompt_config        │
          │ embd_id          │              │ ...                  │
          │ parser_id        │              └──────────────────────┘
          │ doc_num          │
          │ status           │
          └───────┬──────────┘
                  │ 1:N
                  ▼
          ┌──────────────┐     ┌──────────────┐     ┌──────────────┐
          │   Document   │     │    File      │     │    Task      │
          ├──────────────┤     ├──────────────┤     ├──────────────┤
          │ id (PK)      │     │ id (PK)      │     │ id (PK)      │
          │ kb_id (FK)   │     │ parent_id    │     │ doc_id (FK)  │
          │ parser_id    │     │ tenant_id    │     │ task_type    │
          │ name         │     │ name         │     │ progress     │
          │ size         │     │ size         │     │ ...          │
          │ token_num    │     │ type         │     └──────────────┘
          │ chunk_num    │     └──────────────┘
          │ status       │
          └──────────────┘

1.2 用户与租户相关表

表名字段类型说明
user idCHAR(32)主键,UUID
access_tokenVARCHAR(255)API 访问令牌,JWT 签发
nicknameVARCHAR(100)用户昵称
passwordVARCHAR(255)加密密码
emailVARCHAR(255) UNIQUE邮箱(唯一)
avatarTEXT头像 Base64
languageVARCHAR(32)语言偏好
color_schemaVARCHAR(32)主题(Bright|Dark)
timezoneVARCHAR(64)时区
last_login_timeDATETIME最近登录
is_authenticatedCHAR(1)是否通过认证
is_activeCHAR(1)是否激活
is_superuserBOOLEAN是否超级管理员
statusCHAR(1)0=废弃 1=有效
tenant idCHAR(32)主键,UUID
nameVARCHAR(100)租户名称
llm_idVARCHAR(128)默认大模型 ID
embd_idVARCHAR(128)默认嵌入模型 ID
creditINTEGER信用额度(默认 512)
parser_idsVARCHAR(256)文档解析器列表
statusCHAR(1)0=废弃 1=有效
user_tenant idCHAR(32)主键,UUID
user_idCHAR(32)用户 ID(FK → user.id)
tenant_idCHAR(32)租户 ID(FK → tenant.id)
roleVARCHAR(32)角色:owner | admin | normal | invite
invited_byCHAR(32)邀请人 ID

1.3 知识库与文档相关表

表名字段类型说明
knowledgebase idCHAR(32)主键,UUID
avatarTEXT头像 Base64
tenant_idCHAR(32)租户 ID(FK → tenant.id)
nameVARCHAR(128)知识库名称
languageVARCHAR(32)语言
descriptionTEXT描述
embd_idVARCHAR(128)嵌入模型 ID
permissionVARCHAR(16)权限:me | team
created_byCHAR(32)创建者
doc_numINTEGER文档数量
token_numINTEGERToken 总数
chunk_numINTEGERChunk 总数
similarity_thresholdFLOAT相似度阈值
vector_similarity_weightFLOAT向量相似度权重
parser_idCHAR(32)解析器 ID
parser_configJSON解析器配置
pagerankINTEGERPageRank 值
graphrag_task_id
raptor_task_id
mindmap_task_id
CHAR(32)高级 RAG 任务 ID
statusCHAR(1)0=废弃 1=有效
document idCHAR(32)主键,UUID
kb_idCHAR(256)所属知识库 ID
parser_idCHAR(32)解析器 ID
parser_configJSON解析器配置
typeCHAR(32)文件类型
created_byCHAR(32)创建者
nameVARCHAR(255)文档名称
locationVARCHAR(255)存储位置
sizeBIGINT文件大小
token_numINTEGERToken 数
chunk_numINTEGERChunk 数
progressFLOAT处理进度
runCHAR(1)运行状态
content_hashCHAR(32)内容哈希(变更检测)
source_typeVARCHAR(128)文档来源
statusCHAR(1)0=废弃 1=有效

1.4 应用相关表(Dialog / Search / Agent)

表名字段类型说明
dialog idCHAR(32)主键,UUID
tenant_idCHAR(32)租户 ID
nameVARCHAR(255)应用名称
llm_idCHAR(128)LLM ID
llm_settingJSONLLM 参数
prompt_configJSON提示词配置
similarity_thresholdFLOAT相似度阈值
top_nINTEGER返回 Top N
rerank_idCHAR(128)重排序模型
kb_idsJSON关联知识库列表(JSON数组)
statusCHAR(1)0=废弃 1=有效
search idCHAR(32)主键,UUID
tenant_idCHAR(32)租户 ID
nameVARCHAR(128)搜索名称
created_byCHAR(32)创建者
search_configJSON搜索配置(含 kb_ids, doc_ids 等)
statusCHAR(1)0=废弃 1=有效
user_canvas idCHAR(32)主键,UUID
user_idCHAR(255)用户 ID
titleVARCHAR(255)Canvas 标题
permissionCHAR(16)权限:me | team
releaseBOOLEAN是否发布
canvas_typeCHAR(32)Canvas 类型
canvas_categoryCHAR(32)分类:agent_canvas | dataflow_canvas
dslJSON画布 DSL
tagsVARCHAR(512)标签

1.5 Token 与会话表

表名字段类型说明
api_token tenant_idCHAR(32)归属租户(复合主键)
tokenVARCHAR(255)Token 值(复合主键)
dialog_idCHAR(32)关联对话 ID
sourceVARCHAR(16)来源:none | agent | dialog
betaVARCHAR(255)Beta 标记
api_4_conversation idCHAR(32)主键,UUID
dialog_idCHAR(32)对话 ID
user_idCHAR(255)用户标识
messageJSON消息内容
tokensINTEGER消耗 Token
durationFLOAT会话时长
roundINTEGER轮次
thumb_upINTEGER点赞数
sourceVARCHAR(16)来源:none | agent | dialog

1.6 其余辅助表

表名核心字段说明
llm_factoriesname (PK), logo, tags, rankLLM 厂商注册表
llmfid, llm_name (复合PK), model_type, max_tokensLLM 模型定义
tenant_llmid (PK), tenant_id, llm_factory, llm_name, api_key, api_base租户 LLM 配置
tenant_langfusetenant_id (PK), secret_key, public_key, hostLangfuse 可观测配置
tenant_model_providerid, provider_name, tenant_id租户自定义模型供应商
tenant_model_instanceid, instance_name, provider_id, api_key模型实例
tenant_modelid, model_name, provider_id, instance_id, model_type模型定义
tenant_model_groupid, group_type, model_name, strategy模型分组(负载均衡)
tenant_model_group_mappinggroup_id, provider_id, instance_id, model_id, weight (复合PK)模型分组成员
fileid, parent_id, tenant_id, name, location, size, type文件管理
file2documentid, file_id, document_id文件-文档关联
taskid, doc_id, from_page, to_page, task_type, progress文档处理任务
conversationid, dialog_id, name, message, reference, user_id对话记录
canvas_templateid, title, description, canvas_type, dslCanvas 模板
user_canvas_versionid, user_canvas_id, title, dsl, releaseCanvas 版本
mcp_serverid, name, tenant_id, url, server_type, variablesMCP 服务器配置
connectorid, tenant_id, name, source, config数据源连接器
connector2kbid, connector_id, kb_id, auto_parse连接器-KB 关联
sync_logsid, connector_id, kb_id, status, error_msg同步日志
pipeline_operation_logid, document_id, kb_id, parser_id, operation_statusPipeline 操作日志
evaluation_datasetsid, tenant_id, name, kb_ids, created_by评测数据集
evaluation_casesid, dataset_id, question, reference_answer评测用例
evaluation_runsid, dataset_id, dialog_id, metrics_summary, status评测运行
evaluation_resultsid, run_id, case_id, generated_answer, metrics评测结果
memoryid, tenant_id, memory_type, storage_type, permissions记忆模块
system_settingsname (PK), source, data_type, value系统设置
invitation_codeid, code, tenant_id, user_id邀请码

二、现有权限模型分析

当前 RAGFlow 的权限控制粒度及其局限性。

2.1 现有权限层级

🦸

超级管理员 (is_superuser=true)

User 表级别标记,全局最高权限,可跨租户操作。

🏢

租户角色 (UserTenant.role)

在租户范围内定义 4 种角色:owner(拥有者)、admin(管理员)、normal(普通成员)、invite(受邀未激活)。

📚

知识库权限 (Knowledgebase.permission)

仅有 me(仅创建者可见)和 team(整个租户可见)两种粒度。

🎨

Canvas 权限 (UserCanvas.permission)

同样仅有 meteam 两种粒度,与 KB 权限模式相同。

2.2 关键权限检查代码路径

知识库列表可见性过滤

# api/db/services/knowledgebase_service.py - _visibility_and_status_filter
def _visibility_and_status_filter(cls, joined_tenant_ids, user_id):
    return (
        (
            # Team KBs: 租户内所有成员可见
            (cls.model.tenant_id.in_(joined_tenant_ids)
             & (cls.model.permission == 'team'))
            # 或 KB 创建者自己
            | (cls.model.tenant_id == user_id)
        )
        & (cls.model.status == StatusEnum.VALID.value)
    )

知识库访问检查

# api/db/services/knowledgebase_service.py - accessible
def accessible(cls, kb_id, user_id):
    e, kb = cls.get_by_id(kb_id)
    if not e: return False
    if kb.status != StatusEnum.VALID.value: return False
    # 创建者始终可访问
    if kb.tenant_id == user_id: return True
    # 仅 Team KB 允许同租户成员访问
    if kb.permission != 'team': return False
    joined_tenants = TenantService.get_joined_tenants_by_user_id(user_id)
    return any(tenant["tenant_id"] == kb.tenant_id
               for tenant in joined_tenants)

删除权限检查

# api/db/services/knowledgebase_service.py - accessible4deletion
def accessible4deletion(cls, kb_id, user_id):
    # 仅 KB 创建者可以删除
    docs = cls.model.select(cls.model.id).where(
        cls.model.id == kb_id,
        cls.model.created_by == user_id
    ).paginate(0, 1)
    return bool(list(docs.dicts()))

2.3 Dialog 与 KB 关联方式

# Dialog 通过 JSON 字段直接引用 KB IDs,没有任何权限校验中间层
# api/db/db_models.py - Dialog
class Dialog(DataBaseModel):
    kb_ids = JSONField(null=False, default=[])  # 直接存 KB ID 列表

这意味着:一旦 Dialog 配置了某个 KB 的 ID,所有使用该 Dialog 的用户都可以通过它检索该 KB 的内容。没有对 谁能用哪个 Dialog 访问哪个 KB 的精细化控制。

三、需求与差距分析

目标权限能力 vs 现状能力矩阵。

❌ 当前限制

  • KB 权限仅 me / team 两种
  • 无法控制单个 KB 的操作类型(读/写/删/导出)
  • 无法按用户组批量授权
  • 无法自定义角色或权限模板
  • 限流/配额能力
  • Dialog 关联 KB 无权限校验
  • 数据级权限(如某文档/标签可见)
  • 无审计日志
  • API Token 无 KB 范围限制
  • 无法做应用级 KB 隔离

✅ 目标能力

  • 12+ 种操作权限可以独立授予/拒绝
  • 用户、用户组、角色、应用多维度授权
  • 自定义角色,支持权限模板
  • KB 限流:QPS、Token/天、并发上限
  • 应用必须显式授权才能访问 KB
  • 数据级权限:按文档、标签、元数据过滤
  • 完整的审计日志
  • API Token 绑定KB 访问范围
  • 权限继承与覆盖机制
  • 权限变更历史可追溯

核心用例场景

🏭

场景 1: 多部门共享

"财务部"用户组可读写财务KB,"研发部"仅只读,外部顾问无任何权限。

🤖

场景 2: 应用隔离

对外客服机器人(Dialog A)仅能访问"公开FAQ" KB,内部员工助手(Dialog B)可访问全部 KB。

⏱️

场景 3: 分级限流

VIP 客户 API Token 允许 100 QPS/Topic,免费试用账户限 10 QPS 日 1000 次调用。

🔒

场景 4: 数据分级

同一 KB 内,P0 机密文档仅安全组可读,普通文档全员可读。通过元数据标签实现。

四、扩展设计总体方案

采用 RBAC(基于角色的访问控制)+ ABAC(基于属性的访问控制)混合模型,分层设计。

4.1 设计原则

1

向后兼容

所有扩展字段有默认值,原有 me/team 逻辑保留作为快速模式。新表不影响已有 API。

2

分层可插拔

每层权限独立启用/禁用。小团队可只开启最简单模式,企业可开启全部能力。

3

优先拒绝 (Deny-First)

默认无权限,必须显式授权。多条策略聚合使用"最宽松"原则(有任一策略允许即可)。黑名单覆盖白名单。

4

高性能

权限计算结果缓存(Redis),变更时失效。热点路径避免多表 JOIN,使用预计算的权限摘要。

4.2 扩展架构总览


                    ┌──────────────────────────────────────────────────────┐
                    │              ★ 新增层: 权限控制中心 ★                 │
                    └──────────────────────────────────────────────────────┘

 ┌──────────────┐    ┌─────────────────┐    ┌──────────────────────┐
 │  user_group  │    │     role        │    │  role_permission     │
 ├──────────────┤    ├─────────────────┤    ├──────────────────────┤
 │ id (PK)      │    │ id (PK)         │    │ id (PK)              │
 │ tenant_id    │    │ tenant_id       │    │ role_id (FK)         │
 │ name         │    │ name            │    │ resource_type        │
 │ description  │    │ description     │    │ permission           │
 │ created_by   │    │ is_system       │    │ effect: allow|deny   │
 │ status       │    │ parent_role_id  │    │ conditions (JSON)    │
 └──────┬───────┘    │ status          │    └──────────┬───────────┘
        │ N:M        └────────┬────────┘               │ N:1
        ▼                     │                        │
 ┌──────────────┐             │                        │
 │user_group_   │             │                        │
 │  member      │             │                        │
 ├──────────────┤             │                        │
 │ group_id(FK) │             │                        │
 │ user_id (FK) │             │                        │
 └──────────────┘             │                        │
                              ▼                        ▼
 ┌────────────────────────────────────────────────────────────────────────┐
 │                        kb_permission                                   │
 ├────────────────────────────────────────────────────────────────────────┤
 │ id (PK), kb_id (FK), grantee_type, grantee_id, role_id (FK)           │
 │ operations (JSON), data_scope (JSON), effect: allow|deny              │
 │ expires_at, priority, created_by, status                              │
 └────────────────────────────────────────────────────────────────────────┘

 ┌────────────────────────────────────────────────────────────────────────┐
 │                        kb_rate_limit                                   │
 ├────────────────────────────────────────────────────────────────────────┤
 │ id (PK), kb_id (FK), scope_type, scope_id, limit_type                │
 │ max_qps, max_tokens_per_day, max_concurrent, max_requests_per_day     │
 │ window_seconds, enabled, status                                       │
 └────────────────────────────────────────────────────────────────────────┘

 ┌────────────────────────────────────────────────────────────────────────┐
 │                        app_kb_binding                                  │
 ├────────────────────────────────────────────────────────────────────────┤
 │ id (PK), app_type, app_id, kb_id (FK)                                │
 │ operations (JSON), rate_limit_id (FK)                                │
 │ created_by, status                                                    │
 └────────────────────────────────────────────────────────────────────────┘

 ┌────────────────────────────────────────────────────────────────────────┐
 │                        kb_audit_log                                    │
 ├────────────────────────────────────────────────────────────────────────┤
 │ id (PK), kb_id, user_id, app_id, operation, resource                │
 │ result: allow|deny, reason, ip_address, user_agent                   │
 │ created_at                                                           │
 └────────────────────────────────────────────────────────────────────────┘

五、新增表详细设计

5.1 user_group — 用户组 NEW

支持将多个用户组织成组,方便批量授权。组内可包含用户和其他子组(通过 parent_group_id 实现嵌套)。

字段类型可空默认值说明
idCHAR(32)NUUID主键
tenant_idCHAR(32)N-所属租户 (FK → tenant.id)
nameVARCHAR(128)N-组名称(同租户内唯一)
descriptionTEXTYNULL组描述
parent_group_idCHAR(32)YNULL父组 ID(支持嵌套,最多 3 层)
created_byCHAR(32)N-创建者
create_timeBIGINTYNOW创建时间戳
update_timeBIGINTYNOW更新时间戳
statusCHAR(1)Y'1'状态 0=废弃 1=有效
索引设计: (tenant_id, name) UNIQUE; (tenant_id, status); (parent_group_id)

5.2 user_group_member — 组成员 NEW

字段类型可空默认值说明
idCHAR(32)NUUID主键
group_idCHAR(32)N-用户组 ID (FK → user_group.id)
user_idCHAR(32)N-用户 ID (FK → user.id)
create_timeBIGINTYNOW加入时间
statusCHAR(1)Y'1'状态
索引设计: (group_id, user_id) UNIQUE; (user_id)

5.3 role — 自定义角色 NEW

租户可定义自定义角色,配置一组权限。系统预设角色(admin/viewer/editor)不可删除。

字段类型可空默认值说明
idCHAR(32)NUUID主键
tenant_idCHAR(32)N-所属租户
nameVARCHAR(128)N-角色名称(租户内唯一)
descriptionTEXTYNULL描述
is_systemBOOLEANNfalse是否为系统预设(不可删除)
parent_role_idCHAR(32)YNULL继承自哪个角色
created_byCHAR(32)N-创建者
create_timeBIGINTYNOW创建时间
update_timeBIGINTYNOW更新时间
statusCHAR(1)Y'1'状态

5.4 role_permission — 角色权限定义 NEW

定义角色拥有的具体操作权限,支持条件表达式实现 ABAC。

字段类型可空默认值说明
idCHAR(32)NUUID主键
role_idCHAR(32)N-角色 ID (FK → role.id)
resource_typeVARCHAR(32)N-资源类型:knowledgebase, document, dialog, search, file
permissionVARCHAR(64)N-权限标识(见下方权限码表)
effectVARCHAR(8)N'allow'效果:allow | deny
conditionsJSONY{}ABAC 条件(如 {'doc.tag': 'public'})

操作权限码定义

权限码资源类型说明危险级别
kb:readknowledgebase查看知识库列表/详情
kb:createknowledgebase创建新知识库
kb:updateknowledgebase编辑知识库配置(名称、描述、解析器等)
kb:deleteknowledgebase删除知识库
kb:manage_permissionknowledgebase管理知识库权限
kb:exportknowledgebase导出知识库内容
kb:importknowledgebase导入文档到知识库
kb:retrieveknowledgebase通过 Dialog/API 检索知识库
kb:list_docsknowledgebase查看知识库中文档列表
kb:manage_docsknowledgebase管理文档(解析、重新解析、删除文档)
kb:manage_connectorknowledgebase管理数据源连接器
kb:manage_rate_limitknowledgebase管理限流规则
kb:run_evaluationknowledgebase运行评测
kb:use_graphragknowledgebase使用 GraphRAG 功能
kb:use_raptorknowledgebase使用 RAPTOR 功能
kb:use_mindmapknowledgebase使用 Mindmap 功能

5.5 kb_permission — 知识库权限策略 NEW ★核心表★

将角色与具体的知识库绑定,同时指定授权对象(用户/用户组/角色/应用),可设置数据范围和过期时间。

字段类型可空默认值说明
idCHAR(32)NUUID主键
kb_idCHAR(32)N-知识库 ID (FK → knowledgebase.id)
grantee_typeVARCHAR(16)N-授权对象类型:user | group | role | app | token
grantee_idCHAR(32)N-授权对象 ID
role_idCHAR(32)YNULL关联角色 ID (FK → role.id),为 NULL 时需配合 operations 字段使用
operationsJSONYNULL直接指定的操作权限列表:["kb:read","kb:retrieve"]。如果指定了 role_id,则从 role 继承
data_scopeJSONY{}数据范围过滤:{"doc_ids": [...], "tags": ["public"], "meta_filter": {...}}
effectVARCHAR(8)N'allow'效果:allow | deny(deny 优先级高于 allow)
priorityINTEGERN0优先级(数值越大优先级越高,用于多策略排序)
expires_atDATETIMEYNULL过期时间(临时授权场景)
created_byCHAR(32)N-创建者
create_timeBIGINTYNOW创建时间
update_timeBIGINTYNOW更新时间
statusCHAR(1)Y'1'状态
索引设计: (kb_id, grantee_type, grantee_id); (kb_id, effect); (grantee_type, grantee_id); (expires_at)

data_scope 数据范围示例

{
  "doc_ids": ["doc_001", "doc_002"],      // 仅限指定文档
  "exclude_doc_ids": ["doc_secret"],      // 排除特定文档
  "tags": ["public", "internal"],         // 文档标签过滤
  "meta_filter": {                          // 元数据过滤
    "security_level": ["l1", "l2"],
    "department": "engineering"
  },
  "max_token_per_query": 50000          // 单次查询最大 Token
}

5.6 kb_rate_limit — 知识库限流规则 NEW

字段类型可空默认值说明
idCHAR(32)NUUID主键
kb_idCHAR(32)YNULL知识库 ID(NULL 表示全局规则)
nameVARCHAR(128)N-规则名称
scope_typeVARCHAR(16)N'global'作用范围:global | user | group | role | app | token
scope_idCHAR(32)YNULL作用对象 ID
limit_typeVARCHAR(32)N'qps'限流类型:qps | tokens_per_day | requests_per_day | concurrent
max_qpsFLOATYNULL每秒最大查询数
max_tokens_per_dayBIGINTYNULL每日最大 Token 消耗
max_requests_per_dayINTEGERYNULL每日最大请求数
max_concurrentINTEGERYNULL最大并发数
max_tokens_per_requestINTEGERYNULL单次请求最大 Token
window_secondsINTEGERN1滑动窗口大小(秒),用于 QPS 计算
burst_multiplierFLOATN1.0突发倍数(允许短时突发流量)
throttle_actionVARCHAR(16)N'reject'超限动作:reject | queue | degrade
enabledBOOLEANNtrue是否启用
created_byCHAR(32)N-创建者
create_timeBIGINTYNOW创建时间
update_timeBIGINTYNOW更新时间
statusCHAR(1)Y'1'状态
索引设计: (kb_id, scope_type, scope_id); (enabled, status)

限流规则优先级(从高到低)

  1. token 级别(API Token 专属限流)
  2. user 级别(用户专属限流)
  3. app 级别(应用级限流)
  4. group 级别(用户组限流)
  5. kb 全局级别(知识库默认限流)
  6. 租户全局级别

5.7 app_kb_binding — 应用-知识库绑定 NEW ★关键★

替代 Dialog 中 kb_ids JSON 字段的硬编码方式。应用必须先建立绑定关系才能检索知识库。

字段类型可空默认值说明
idCHAR(32)NUUID主键
app_typeVARCHAR(16)N-应用类型:dialog | search | agent | api
app_idCHAR(32)N-应用 ID(dialog.id / search.id / user_canvas.id)
kb_idCHAR(32)N-知识库 ID (FK → knowledgebase.id)
operationsJSONY["kb:retrieve"]允许的操作(通常只用 kb:retrieve)
rate_limit_idCHAR(32)YNULL关联的限流规则 (FK → kb_rate_limit.id)
data_scopeJSONY{}应用级别的数据范围(如客服机器人只能看 FAQ 类文档)
created_byCHAR(32)N-创建者
create_timeBIGINTYNOW创建时间
update_timeBIGINTYNOW更新时间
statusCHAR(1)Y'1'状态
迁移说明:现有 Dialog 的 kb_ids JSON 字段中的数据需要迁移到 app_kb_binding 表。迁移期间 Dialog 同时支持两种模式。迁移完成后废弃 kb_ids 字段。

5.8 kb_audit_log — 审计日志 NEW

字段类型可空默认值说明
idCHAR(32)NUUID主键
tenant_idCHAR(32)N-租户 ID
kb_idCHAR(32)YNULL知识库 ID
user_idCHAR(32)YNULL操作用户 ID
app_idCHAR(32)YNULL应用 ID(如果是通过应用访问)
app_typeVARCHAR(16)YNULL应用类型
operationVARCHAR(64)N-操作码(如 kb:read, kb:retrieve)
resourceVARCHAR(255)YNULL操作的资源路径
detailJSONY{}操作详情(请求内容摘要等)
resultVARCHAR(8)N-结果:allow | deny | error
reasonVARCHAR(255)YNULL拒绝/错误原因
ip_addressVARCHAR(45)YNULL请求 IP
user_agentVARCHAR(512)YNULLUser-Agent
latency_msINTEGERYNULL耗时(毫秒)
token_usedINTEGERYNULL消耗 Token
created_atDATETIMENNOW创建时间(建议按月分表)
存储策略:建议按月分表 (kb_audit_log_202606),定期归档(保留 6 个月热数据,1 年冷数据)。支持异步写入(Redis Stream → 批量 INSERT)。

5.9 现有表字段改动

表名改动类型字段说明
knowledgebase MODIFY permission me | team 扩展为 me | team | custom
custom 模式下启用细粒度权限表,me/team 保留作为快速模式。
knowledgebase ADD default_rate_limit_id 关联默认限流规则 (FK → kb_rate_limit.id)
api_token ADD kb_scope JSON: {"kb_ids":["*"], "operations":["kb:retrieve"], "max_qps":10}
限制 Token 可访问的 KB 范围和操作
dialog DEPRECATE kb_ids 逐步废弃,由 app_kb_binding 替代
user_tenant ADD role_id 关联自定义角色 (FK → role.id),为 NULL 时使用原有 4 种角色

六、权限检查流程

6.1 权限检查入口——统一鉴权中间件

async def check_kb_permission(
    user_id: str,
    kb_id: str,
    operation: str,
    context: dict = {}   # {app_type, app_id, token_id, ...}
) -> PermissionResult:
    
    统一的知识库权限检查入口。
    返回 (allowed: bool, data_scope: dict, rate_limit: RateLimitConfig)
    """

    # Step 0: 超级管理员直接放行
    if is_superuser(user_id):
        return ALLOWED_WITH_FULL_SCOPE

    # Step 1: 检查知识库是否存在且有效
    kb = get_kb(kb_id)
    if not kb or kb.status != '1':
        return DENIED("KB not found or disabled")

    # Step 2: 快速模式 - me/team 权限(无 custom 配置时生效)
    if kb.permission != 'custom':
        return check_legacy_permission(kb, user_id, operation)

    # Step 3: Custom 模式 - 收集所有匹配的策略
    policies = collect_matching_policies(
        kb_id=kb_id,
        user_id=user_id,
        user_groups=get_user_groups(user_id),
        user_roles=get_user_roles(user_id, kb.tenant_id),
        app_type=context.get('app_type'),
        app_id=context.get('app_id'),
        token_id=context.get('token_id'),
        operation=operation,
    )

    # Step 4: 按优先级排序,deny 优先
    policies = sorted(policies, key=lambda p: (
        0 if p.effect == 'deny' else 1,  # deny 优先
        -p.priority                                    # 同类型内按优先级
    ))

    # Step 5: 评估第一条决定性策略
    for policy in policies:
        # ABAC 条件评估
        if policy.conditions and not evaluate_conditions(policy.conditions, context):
            continue

        if policy.effect == 'deny':
            return DENIED(f"Denied by policy {policy.id}")

        # allow 策略:合并数据范围
        data_scope = merge_data_scopes([
            p.data_scope for p in policies if p.effect == 'allow'
        ])

        # 获取对应的限流配置
        rate_limit = get_effective_rate_limit(kb_id, user_id, context)

        return ALLOWED(data_scope=data_scope, rate_limit=rate_limit)

    # Step 6: 无匹配策略 → 默认拒绝
    return DENIED("No matching permission policy")

6.2 策略匹配收集

def collect_matching_policies(kb_id, user_id, user_groups, user_roles,
                               app_type, app_id, token_id, operation):
    
    从 kb_permission 表查找所有可能匹配的策略。
    按授权对象类型分别匹配。
    """
    policies = []

    # 查询 kb_permission 表中该 KB 的所有有效策略
    all_kb_policies = KB_Permission.query(
        kb_id=kb_id,
        status='1',
        # 过滤已过期策略
        where_clause="(expires_at IS NULL OR expires_at > NOW())"
    )

    for policy in all_kb_policies:
        matched = False

        if policy.grantee_type == 'user' and policy.grantee_id == user_id:
            matched = True
        elif policy.grantee_type == 'group' and policy.grantee_id in user_groups:
            matched = True
        elif policy.grantee_type == 'role' and policy.grantee_id in user_roles:
            matched = True
        elif policy.grantee_type == 'app' and f"{app_type}:{app_id}" == policy.grantee_id:
            matched = True
        elif policy.grantee_type == 'token' and policy.grantee_id == token_id:
            matched = True

        if matched:
            # 如果策略关联了 role,从 role_permission 加载具体权限
            if policy.role_id:
                role_ops = get_role_permissions(policy.role_id, 'knowledgebase')
                if operation in role_ops['permissions']:
                    policies.append(policy)
            elif policy.operations and operation in policy.operations:
                policies.append(policy)

    return policies

6.3 限流检查流程

def check_rate_limit(kb_id, user_id, scope_type, scope_id) -> bool:
    
    检查当前请求是否超过限流阈值。
    使用 Redis 滑动窗口实现。
    """
    # 获取所有匹配的限流规则(从最特化到最通用)
    rules = get_rate_limit_rules(kb_id, scope_type, scope_id)

    for rule in rules:
        if not rule.enabled:
            continue

        # Redis 滑动窗口 Key
        window_key = f"ratelimit:{rule.id}:{scope_id}"

        if rule.limit_type == 'qps':
            current = redis_incr_window(window_key, rule.window_seconds)
            if current > rule.max_qps * rule.burst_multiplier:
                return False  # 限流触发

        elif rule.limit_type == 'tokens_per_day':
            day_key = f"ratelimit:{rule.id}:{scope_id}:tokens:{today()}"
            current = redis_get(day_key) or 0
            if current > rule.max_tokens_per_day:
                return False

        # ... 其他限流类型类似

    return True  # 未触发限流

七、迁移与兼容性策略

7.1 向后兼容保障

🔄

双模式运行

新增 FEATURE_FINE_GRAINED_PERMISSION 功能开关。关闭时完全按原有 me/team 逻辑运行,零性能损耗。

📊

数据自动迁移

启动时检查是否需要迁移:将 permission=team 的 KB 自动生成一条 kb_permission 记录,授权给租户全部成员。

🔌

Dialog kb_ids 兼容

并存期内,Dialog 优先读 app_kb_binding 表;若无绑定记录,降级使用 kb_ids JSON 字段。

🛡️

默认策略兜底

开启细粒度权限后,若 KB 无任何 kb_permission 记录,默认仅 KB 创建者可访问(安全优先)。

7.2 迁移步骤

1

Phase 1: 建表与双写(v2.0)

创建所有新表。KB 创建/更新时同时维护 legacy permission 字段和新的 kb_permission 记录。

2

Phase 2: 灰度读(v2.1)

按租户灰度开启细粒度鉴权。监控性能与错误率。修复问题。

3

Phase 3: 全量切换(v2.2)

默认开启细粒度权限。遗留 permission 字段标记为 deprecated。提供一键迁移脚本。

4

Phase 4: 清理(v3.0)

移除 legacy 权限代码。Dialog.kb_ids 字段删除。完成全部迁移。

7.3 Peewee 模型代码示例

# 新增模型定义(添加到 api/db/db_models.py)

class UserGroup(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    tenant_id = CharField(max_length=32, null=False, index=True)
    name = CharField(max_length=128, null=False, index=True)
    description = TextField(null=True)
    parent_group_id = CharField(max_length=32, null=True, index=True)
    created_by = CharField(max_length=32, null=False, index=True)
    status = CharField(max_length=1, null=True, default="1", index=True)

    class Meta:
        db_table = "user_group"
        indexes = (
            (("tenant_id", "name"), True),  # UNIQUE
        )

class UserGroupMember(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    group_id = CharField(max_length=32, null=False, index=True)
    user_id = CharField(max_length=32, null=False, index=True)
    status = CharField(max_length=1, null=True, default="1", index=True)

    class Meta:
        db_table = "user_group_member"
        indexes = (
            (("group_id", "user_id"), True),
        )

class Role(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    tenant_id = CharField(max_length=32, null=False, index=True)
    name = CharField(max_length=128, null=False, index=True)
    description = TextField(null=True)
    is_system = BooleanField(null=False, default=False)
    parent_role_id = CharField(max_length=32, null=True, index=True)
    created_by = CharField(max_length=32, null=False, index=True)
    status = CharField(max_length=1, null=True, default="1", index=True)

    class Meta:
        db_table = "role"
        indexes = (
            (("tenant_id", "name"), True),
        )

class RolePermission(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    role_id = CharField(max_length=32, null=False, index=True)
    resource_type = CharField(max_length=32, null=False, index=True)
    permission = CharField(max_length=64, null=False, index=True)
    effect = CharField(max_length=8, null=False, default="allow")
    conditions = JSONField(null=True, default={})

    class Meta:
        db_table = "role_permission"

class KBPermission(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    kb_id = CharField(max_length=32, null=False, index=True)
    grantee_type = CharField(max_length=16, null=False, index=True)
    grantee_id = CharField(max_length=32, null=False, index=True)
    role_id = CharField(max_length=32, null=True, index=True)
    operations = JSONField(null=True, default=[])
    data_scope = JSONField(null=True, default={})
    effect = CharField(max_length=8, null=False, default="allow")
    priority = IntegerField(null=False, default=0)
    expires_at = DateTimeField(null=True, index=True)
    created_by = CharField(max_length=32, null=False, index=True)
    status = CharField(max_length=1, null=True, default="1", index=True)

    class Meta:
        db_table = "kb_permission"

class KBRateLimit(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    kb_id = CharField(max_length=32, null=True, index=True)
    name = CharField(max_length=128, null=False)
    scope_type = CharField(max_length=16, null=False, default="global", index=True)
    scope_id = CharField(max_length=32, null=True, index=True)
    limit_type = CharField(max_length=32, null=False, default="qps")
    max_qps = FloatField(null=True)
    max_tokens_per_day = BigIntegerField(null=True)
    max_requests_per_day = IntegerField(null=True)
    max_concurrent = IntegerField(null=True)
    max_tokens_per_request = IntegerField(null=True)
    window_seconds = IntegerField(null=False, default=1)
    burst_multiplier = FloatField(null=False, default=1.0)
    throttle_action = CharField(max_length=16, null=False, default="reject")
    enabled = BooleanField(null=False, default=True)
    created_by = CharField(max_length=32, null=False, index=True)
    status = CharField(max_length=1, null=True, default="1", index=True)

    class Meta:
        db_table = "kb_rate_limit"

class AppKBBinding(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    app_type = CharField(max_length=16, null=False, index=True)
    app_id = CharField(max_length=32, null=False, index=True)
    kb_id = CharField(max_length=32, null=False, index=True)
    operations = JSONField(null=True, default=["kb:retrieve"])
    rate_limit_id = CharField(max_length=32, null=True, index=True)
    data_scope = JSONField(null=True, default={})
    created_by = CharField(max_length=32, null=False, index=True)
    status = CharField(max_length=1, null=True, default="1", index=True)

    class Meta:
        db_table = "app_kb_binding"
        indexes = (
            (("app_type", "app_id", "kb_id"), True),
        )

class KBAuditLog(DataBaseModel):
    id = CharField(max_length=32, primary_key=True)
    tenant_id = CharField(max_length=32, null=False, index=True)
    kb_id = CharField(max_length=32, null=True, index=True)
    user_id = CharField(max_length=32, null=True, index=True)
    app_id = CharField(max_length=32, null=True, index=True)
    app_type = CharField(max_length=16, null=True)
    operation = CharField(max_length=64, null=False, index=True)
    resource = CharField(max_length=255, null=True)
    detail = JSONField(null=True, default={})
    result = CharField(max_length=8, null=False, index=True)
    reason = CharField(max_length=255, null=True)
    ip_address = CharField(max_length=45, null=True)
    user_agent = CharField(max_length=512, null=True)
    latency_ms = IntegerField(null=True)
    token_used = IntegerField(null=True)
    created_at = DateTimeField(null=False, index=True, default=datetime.now)

    class Meta:
        db_table = "kb_audit_log"

八、实施路线图

8.1 分阶段计划

阶段版本内容优先级预估工期
Phase 1 v2.0-alpha 基础设施搭建
• 新增 user_group, user_group_member 表及 CRUD API
• 新增 role, role_permission 表及 CRUD API
• 新增 kb_permission 表及核心鉴权 API
• 新增 app_kb_binding 表及绑定管理 API
• 实现统一鉴权中间件 check_kb_permission
• 默认关闭细粒度权限(feature flag)
P0 4-5 周
Phase 2 v2.0-beta 限流与审计
• 新增 kb_rate_limit 表及 CRUD API
• 实现 Redis 滑动窗口限流器
• 新增 kb_audit_log 表及异步日志写入
• Legacy → new 数据迁移脚本
• 管理后台 UI: 用户组管理页面
P0 4-5 周
Phase 3 v2.1 前端与灰度
• KB 细粒度权限配置 UI(替换 me/team 下拉框)
• Dialog/Search 绑定 KB UI 改动
• 限流规则配置 UI
• Token KB 范围配置 UI
• 按租户灰度开启 + 监控
• 审计日志查询界面
P1 5-6 周
Phase 4 v2.2 高级特性
• ABAC 条件表达式引擎
• 数据级权限(含 document tag 过滤)
• 权限模板/权限集(一键授权)
• 权限变更通知
• 权限导入/导出
• 性能优化(权限缓存层)
P1 5-6 周
Phase 5 v3.0 清理与收尾
• 移除遗留 permission 字段
• 移除 Dialog.kb_ids JSON 字段
• 全量默认开启细粒度权限
• 文档与 SDK 更新
P2 2-3 周

8.2 系统预设角色

角色名称is_system包含权限说明
KB Admintrue kb:read, kb:update, kb:delete, kb:manage_permission, kb:export, kb:import, kb:list_docs, kb:manage_docs, kb:manage_connector, kb:manage_rate_limit, kb:retrieve, kb:run_evaluation, kb:use_graphrag, kb:use_raptor, kb:use_mindmap 知识库完全管理权限
KB Editortrue kb:read, kb:update, kb:import, kb:list_docs, kb:manage_docs, kb:manage_connector, kb:retrieve, kb:run_evaluation 可编辑、导入文档,无法删除 KB 和管理权限
KB Viewertrue kb:read, kb:list_docs, kb:retrieve 只读访问(查看和检索)
KB Retrievertrue kb:retrieve 仅检索(适用于 API Token)
KB No Accesstrue 显式禁止访问(deny 策略)

8.3 性能考量

权限缓存

每个用户的 KB 权限计算结果缓存在 Redis,TTL 5 分钟。权限变更时主动失效。热点 KB 权限可本地内存缓存。

📦

批量预计算

用户登录时异步预计算该用户对所有 KB 的有效权限摘要({kb_id: {operations: [...], data_scope: {...}}}),存入 Redis。

🔍

查询优化

kb_permission 按 (kb_id, grantee_type, grantee_id) 复合索引查询;app_kb_binding 按 (app_type, app_id) 查询。

📝

审计日志异步

审计日志通过 Redis Stream 异步批量写入,不影响请求主链路。高峰期可降级采样写入。

8.4 关键设计决策总结

决策点选择理由
权限模型RBAC + ABAC 混合RBAC 覆盖角色层面的批量管理,ABAC 覆盖按属性的动态过滤
策略效果Deny 优先于 Allow安全优先:一条 deny 策略即可阻止访问
策略冲突高优先级覆盖低优先级允许精细化例外处理
默认行为默认拒绝(无策略 = 不可访问)显式授权原则,避免权限泄露
限流实现Redis 滑动窗口高性能、分布式友好、精确
权限缓存Redis + 本地内存双层减少 DB 压力,支持高 QPS 场景
向后兼容Feature flag + 双模式平滑迁移,零停机
审计日志异步批量写 + 按月分表不影响主链路性能,便于归档管理