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,167 @@
|
||||
package accesscontrol
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/accesspolicy"
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func requiredPermission(method, path, kind string, actions ...string) string {
|
||||
path = strings.TrimPrefix(path, "/api/v1/")
|
||||
mutating := method != http.MethodGet && method != http.MethodHead
|
||||
if strings.HasPrefix(path, "workbench/resources/") && strings.HasSuffix(path, "/export") {
|
||||
return "content.deliver"
|
||||
}
|
||||
if method != http.MethodGet && method != http.MethodHead && len(actions) > 0 &&
|
||||
(strings.Contains(path, "/actions/") || strings.HasPrefix(path, "workbench/jobs/")) {
|
||||
return accesspolicy.WorkbenchActionPermission(kind, actions[0])
|
||||
}
|
||||
module := ""
|
||||
switch {
|
||||
case strings.HasPrefix(path, "access/"):
|
||||
return ""
|
||||
case strings.HasPrefix(path, "workbench/resources/"):
|
||||
if method == http.MethodDelete {
|
||||
if kind == "flows" {
|
||||
return "flows.delete"
|
||||
}
|
||||
return "content.delete"
|
||||
}
|
||||
if mutating && kind == "training-maps" {
|
||||
return "content.maps"
|
||||
}
|
||||
if kind == "flows" {
|
||||
module = "flows"
|
||||
} else {
|
||||
module = "content"
|
||||
}
|
||||
case strings.HasPrefix(path, "workbench/jobs/"):
|
||||
if kind == "flows" {
|
||||
module = "flows"
|
||||
} else {
|
||||
module = "content"
|
||||
}
|
||||
case strings.HasPrefix(path, "digital-humans"), strings.HasPrefix(path, "im/"), strings.HasPrefix(path, "im-channels"), strings.HasPrefix(path, "wechat"):
|
||||
if strings.Contains(path, "/wechat/") || strings.HasPrefix(path, "wechat") || strings.HasPrefix(path, "im/") || strings.HasPrefix(path, "im-channels") {
|
||||
return "digital-humans.wechat"
|
||||
}
|
||||
if mutating && (strings.HasSuffix(path, "/distill") || strings.HasSuffix(path, "/trial")) {
|
||||
return "digital-humans.distill"
|
||||
}
|
||||
if mutating && strings.HasSuffix(path, "/review") {
|
||||
return "digital-humans.review"
|
||||
}
|
||||
if method == http.MethodDelete && !strings.Contains(path, "/bindings/") {
|
||||
return "digital-humans.delete"
|
||||
}
|
||||
module = "digital-humans"
|
||||
case path == "workbench/operations/consumption":
|
||||
return "knowledge.consumption"
|
||||
case strings.HasPrefix(path, "workbench/operations/memories/"):
|
||||
return "agents.memory"
|
||||
case strings.HasPrefix(path, "workbench/operations/accounts"), strings.HasPrefix(path, "workbench/operations/grants"), strings.HasPrefix(path, "workbench/operations/quotas"):
|
||||
module = "balance"
|
||||
case strings.HasPrefix(path, "knowledge-chat"), strings.HasPrefix(path, "agent-chat"), strings.HasPrefix(path, "agent/"):
|
||||
module = "chat"
|
||||
case strings.HasPrefix(path, "knowledge"), strings.HasPrefix(path, "shared-knowledge-bases"), strings.HasPrefix(path, "chunks"), strings.HasPrefix(path, "tags"), strings.HasPrefix(path, "faq"), strings.HasPrefix(path, "initialization/config/"), strings.HasPrefix(path, "initialization/initialize/"):
|
||||
module = "knowledge"
|
||||
case strings.HasPrefix(path, "agents"), strings.HasPrefix(path, "shared-agents"), strings.HasPrefix(path, "embed-channels"), strings.HasPrefix(path, "custom-agents"), strings.HasPrefix(path, "skills"), strings.HasPrefix(path, "instruction-skills"), strings.HasPrefix(path, "memory"):
|
||||
if mutating {
|
||||
if strings.HasPrefix(path, "skills/instructions") || strings.HasPrefix(path, "instruction-skills") {
|
||||
return "agents.skills"
|
||||
}
|
||||
if strings.HasPrefix(path, "memory") {
|
||||
return "agents.memory"
|
||||
}
|
||||
if method == http.MethodDelete && path == "agents/:id" {
|
||||
return "agents.delete"
|
||||
}
|
||||
}
|
||||
module = "agents"
|
||||
case strings.HasPrefix(path, "sessions"), strings.HasPrefix(path, "messages"), strings.HasPrefix(path, "chat"):
|
||||
module = "chat"
|
||||
case strings.HasPrefix(path, "organizations"):
|
||||
module = "sharing"
|
||||
case path == "evaluation", strings.HasPrefix(path, "tenants/kv/") && mutating, strings.HasPrefix(path, "tenants/") && (strings.Contains(path, "/members") || strings.Contains(path, "/invitations")), strings.HasPrefix(path, "me/env-vars"):
|
||||
module = "settings"
|
||||
case strings.HasPrefix(path, "models"), strings.HasPrefix(path, "mcp-services"), strings.HasPrefix(path, "sandbox-configs"), strings.HasPrefix(path, "vector-stores"), strings.HasPrefix(path, "storage-backends"), strings.HasPrefix(path, "web-search-providers"), strings.HasPrefix(path, "datasource"):
|
||||
// Read-only selectors are used by content/Agent setup as well as settings.
|
||||
if method == http.MethodGet || method == http.MethodHead {
|
||||
return ""
|
||||
}
|
||||
module = "settings"
|
||||
}
|
||||
if module == "" {
|
||||
return ""
|
||||
}
|
||||
action := "read"
|
||||
if method != http.MethodGet && method != http.MethodHead && !strings.HasSuffix(path, "/search") && !strings.HasSuffix(path, "/hybrid-search") && path != "knowledge-search" {
|
||||
action = "write"
|
||||
}
|
||||
return module + "." + action
|
||||
}
|
||||
|
||||
func (s *Service) Middleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
t, u := identity(c.Request.Context())
|
||||
if t == 0 || u == "" || types.IsSyntheticUserID(u) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
path := c.FullPath()
|
||||
if strings.Contains(path, "/auth/") || path == "/api/v1/access/me" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
snapshot, err := s.Snapshot(c.Request.Context())
|
||||
if err != nil {
|
||||
fail(c, 503, "权限信息暂不可用,请稍后重试")
|
||||
return
|
||||
}
|
||||
ctx := accesspolicy.WithSnapshot(c.Request.Context(), snapshot)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
kind := c.Param("kind")
|
||||
action := c.Param("action")
|
||||
if strings.Contains(path, "/workbench/jobs/") {
|
||||
var row struct{ Kind, Action string }
|
||||
if e := s.db.Table("workbench_jobs").Select("kind, action").Where("tenant_id=? AND owner_id=? AND id=?", t, u, c.Param("id")).Take(&row).Error; e == nil {
|
||||
kind = row.Kind
|
||||
action = row.Action
|
||||
}
|
||||
}
|
||||
permission := requiredPermission(c.Request.Method, path, kind, action)
|
||||
if strings.HasSuffix(path, "/operations/records/:recordKind") {
|
||||
switch c.Param("recordKind") {
|
||||
case "transactions":
|
||||
permission = "balance.read"
|
||||
case "usage":
|
||||
permission = "usage.read"
|
||||
case "audit":
|
||||
permission = "audit.read"
|
||||
case "system":
|
||||
permission = "system.read"
|
||||
case "consumption":
|
||||
permission = "knowledge.read"
|
||||
case "memories":
|
||||
permission = "agents.read"
|
||||
}
|
||||
}
|
||||
if permission != "" && ((!administrator(ctx) && adminOnlyPermission(permission)) || !accesspolicy.Allowed(ctx, permission)) {
|
||||
label := permission
|
||||
for _, m := range Catalog {
|
||||
for _, p := range m.Permissions {
|
||||
if p.ID == permission {
|
||||
label = m.Name + " / " + p.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
fail(c, 403, "没有权限:"+label)
|
||||
c.Set("required_permission_label", label)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user