package accesscontrol import ( "strings" "time" "github.com/Tencent/WeKnora/internal/types" ) type Role struct { ID string `json:"id" gorm:"primaryKey"` TenantID uint64 `json:"tenant_id"` Name string `json:"name"` Description string `json:"description"` Permissions types.StringArray `json:"permissions" gorm:"type:jsonb"` Version int `json:"version"` PermissionVersion int `json:"permission_version" gorm:"not null;default:0"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } func (Role) TableName() string { return "workbench_access_roles" } type Assignment struct { TenantID uint64 `gorm:"primaryKey"` UserID string `gorm:"primaryKey"` RoleID string } func (Assignment) TableName() string { return "workbench_access_assignments" } type Publication struct { TenantID uint64 `json:"tenant_id" gorm:"primaryKey"` Kind string `json:"kind" gorm:"primaryKey"` ResourceID string `json:"resource_id" gorm:"primaryKey"` Global bool `json:"global"` UpdatedAt time.Time `json:"updated_at"` } func (Publication) TableName() string { return "workbench_access_publications" } type Grant struct { TenantID uint64 `json:"tenant_id" gorm:"primaryKey"` Kind string `json:"kind" gorm:"primaryKey"` ResourceID string `json:"resource_id" gorm:"primaryKey"` UserID string `json:"user_id" gorm:"primaryKey"` UpdatedAt time.Time `json:"updated_at"` } func (Grant) TableName() string { return "workbench_access_grants" } type Permission struct { ID string `json:"id"` Name string `json:"name"` } type Module struct { ID string `json:"id"` Name string `json:"name"` Permissions []Permission `json:"permissions"` AdminOnly bool `json:"admin_only,omitempty"` } var Catalog = []Module{ {"chat", "对话", []Permission{{"chat.read", "查看对话"}, {"chat.write", "发起对话与生成"}}, false}, {"content", "内容任务", []Permission{{"content.read", "查看内容任务与训练营导图"}, {"content.write", "创建与编辑内容任务"}, {"content.generate", "拆解、生成与修改内容"}, {"content.maps", "创建、生成与修改训练营导图"}, {"content.review", "采用、跳过与跨稿质检"}, {"content.delete", "删除内容任务与训练营导图"}, {"content.deliver", "复制与下载成稿"}}, false}, {"flows", "画布编排", []Permission{{"flows.read", "查看画布"}, {"flows.write", "创建与修改画布"}, {"flows.delete", "删除画布"}}, false}, {"digital-humans", "数字人", []Permission{{"digital-humans.read", "查看数字人与蒸馏结果"}, {"digital-humans.write", "创建、编辑与绑定 Agent"}, {"digital-humans.distill", "提交资料、蒸馏与试写"}, {"digital-humans.review", "审核蒸馏版本"}, {"digital-humans.wechat", "连接与管理微信账号"}, {"digital-humans.delete", "删除数字人"}}, false}, {"knowledge", "知识库", []Permission{{"knowledge.read", "查看、检索、Wiki 与知识图谱"}, {"knowledge.write", "上传、分类、编辑与删除知识"}, {"knowledge.consumption", "开放与禁用自己的消费记录"}}, false}, {"agents", "Agent 与技能", []Permission{{"agents.read", "查看 Agent、技能与记忆"}, {"agents.write", "手动、AI 创建与配置 Agent"}, {"agents.skills", "导入、挂载与删除 Markdown 技能"}, {"agents.memory", "编辑、审核与整理记忆"}, {"agents.delete", "删除 Agent"}}, false}, {"sharing", "共享空间", []Permission{{"sharing.read", "查看共享空间"}, {"sharing.write", "管理共享(仍受空间管理员限制)"}}, false}, {"settings", "设置", []Permission{{"settings.read", "查看空间设置"}, {"settings.write", "修改空间设置"}}, true}, {"access", "访问控制", []Permission{{"access.read", "查看用户、角色与资源授权"}, {"access.write", "管理用户、角色与资源授权"}}, true}, {"balance", "余额", []Permission{{"balance.read", "查看全部用户余额与发放记录"}, {"balance.write", "发放余额与设置额度"}}, true}, {"usage", "用量分析", []Permission{{"usage.read", "查看模型用量与费用明细"}}, true}, {"audit", "操作日志", []Permission{{"audit.read", "查看用户操作记录"}}, true}, {"system", "系统错误日志", []Permission{{"system.read", "查看运行错误与 AI 错误"}}, true}, } const currentPermissionVersion = 2 // Legacy roles used one write permission for all actions in a module. // Expand only unsaved legacy rows so deselecting a new permission is effective. func effectiveRolePermissions(role Role) types.StringArray { permissions := append(types.StringArray{}, role.Permissions...) if role.PermissionVersion >= currentPermissionVersion { return permissions } selected := map[string]bool{} for _, permission := range permissions { selected[permission] = true } for _, module := range Catalog { if !selected[module.ID+".read"] || !selected[module.ID+".write"] || module.AdminOnly { continue } for _, permission := range module.Permissions { if selected[permission.ID] || permission.ID == "content.review" || permission.ID == "content.deliver" { continue } permissions = append(permissions, permission.ID) selected[permission.ID] = true } } return permissions } func adminOnlyPermission(permission string) bool { for _, module := range Catalog { if strings.HasPrefix(permission, module.ID+".") { return module.AdminOnly } } return false } func validPermissions(values []string) bool { known := map[string]bool{} for _, m := range Catalog { for _, p := range m.Permissions { known[p.ID] = true } } selected := map[string]bool{} for _, v := range values { if !known[v] { return false } selected[v] = true } for _, value := range values { module, _, _ := strings.Cut(value, ".") if !strings.HasSuffix(value, ".read") && !selected[module+".read"] { return false } } return true }