WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user