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
86 lines
3.2 KiB
Go
86 lines
3.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"github.com/Tencent/WeKnora/internal/accesspolicy"
|
|
apperrors "github.com/Tencent/WeKnora/internal/errors"
|
|
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
)
|
|
|
|
func assetAdministrator(ctx context.Context) bool {
|
|
return types.IsSystemAdminFromContext(ctx) || types.TenantRoleFromContext(ctx).HasPermission(types.TenantRoleAdmin)
|
|
}
|
|
|
|
func assetOwnedByCaller(ctx context.Context, tenant uint64, owner string) bool {
|
|
current, ok := types.TenantIDFromContext(ctx)
|
|
if !ok || current == 0 || current != tenant {
|
|
return false
|
|
}
|
|
if assetAdministrator(ctx) {
|
|
return true
|
|
}
|
|
user, ok := types.UserIDFromContext(ctx)
|
|
return ok && user != "" && !types.IsSyntheticUserID(user) && owner != "" && owner == user
|
|
}
|
|
|
|
// CheckKnowledgeBaseCopyPermission is used before enqueueing and again by the
|
|
// worker. Jobs without a human initiator retain their internal tenant scope.
|
|
func CheckKnowledgeBaseCopyPermission(ctx context.Context, kb *types.KnowledgeBase) error {
|
|
tenant, ok := types.TenantIDFromContext(ctx)
|
|
if kb == nil || !ok || tenant == 0 || kb.TenantID != tenant {
|
|
return apperrors.NewForbiddenError("没有此知识库的复制权限")
|
|
}
|
|
if assetAdministrator(ctx) {
|
|
return nil
|
|
}
|
|
user, hasUser := types.UserIDFromContext(ctx)
|
|
if _, apiKey := types.TenantAPIKeyScopeFromContext(ctx); apiKey {
|
|
return types.AuthorizeTenantAPIKeyKnowledgeBases(ctx, kb.ID)
|
|
}
|
|
if !hasUser && accesspolicy.FromContext(ctx) == nil {
|
|
return nil
|
|
}
|
|
if user == "" || types.IsSyntheticUserID(user) || user != kb.CreatorID {
|
|
return apperrors.NewForbiddenError("共享知识库仅支持查看和使用,不能复制")
|
|
}
|
|
if snapshot := accesspolicy.FromContext(ctx); snapshot != nil && snapshot.NoExport["knowledge:"+kb.ID] {
|
|
return apperrors.NewForbiddenError("此知识库不允许复制或导出")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func nativeAgentReadable(ctx context.Context, agent *types.CustomAgent) bool {
|
|
if agent == nil {
|
|
return false
|
|
}
|
|
agent.EnsureDefaults()
|
|
tenant, ok := types.TenantIDFromContext(ctx)
|
|
if !agent.Enabled && !assetOwnedByCaller(ctx, agent.TenantID, agent.CreatedBy) {
|
|
return false
|
|
}
|
|
return ok && tenant != 0 && tenant == agent.TenantID &&
|
|
accesspolicy.Allowed(ctx, "agents.read") && accesspolicy.Readable(ctx, "agents", agent.ID, assetOwnedByCaller(ctx, tenant, agent.CreatedBy), agent.Visibility == "global" && agent.Enabled)
|
|
}
|
|
|
|
func nativeAgentExecutableDependency(ctx context.Context, agent *types.CustomAgent) bool {
|
|
if agent == nil {
|
|
return false
|
|
}
|
|
agent.EnsureDefaults()
|
|
return agent.Enabled && accesspolicy.CanExecuteDependency(ctx, agent.TenantID, "agents", agent.ID, agent.CreatedBy)
|
|
}
|
|
|
|
func instructionSkillReadable(ctx context.Context, skill *types.TenantSkillCatalogEntity) bool {
|
|
if skill == nil || !skill.InstructionOnly {
|
|
return false
|
|
}
|
|
skill.EnsureInstructionDefaults()
|
|
tenant, ok := types.TenantIDFromContext(ctx)
|
|
if !skill.Enabled && !assetOwnedByCaller(ctx, skill.TenantID, skill.CreatedBy) {
|
|
return false
|
|
}
|
|
return ok && tenant != 0 && tenant == skill.TenantID &&
|
|
accesspolicy.Allowed(ctx, "agents.read") && accesspolicy.Readable(ctx, "skills", skill.ID, assetOwnedByCaller(ctx, tenant, skill.CreatedBy), skill.Visibility == "global" && skill.Enabled)
|
|
}
|