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
73 lines
2.9 KiB
Go
73 lines
2.9 KiB
Go
package workbench
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestAdministratorCanManageTenantResourcesWithoutChangingOwner(t *testing.T) {
|
|
s := sqliteWorkbench(t)
|
|
r := seedResource(t, s, 1, "contributor", "training-maps")
|
|
ctx := workbenchContext(1, "admin", types.TenantRoleAdmin)
|
|
if _, err := s.get(ctx, r.Kind, r.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := lockResource(ctx, s.db, r.Kind, r.ID); err != nil {
|
|
t.Fatalf("administrator cannot lock tenant resource for writing: %v", err)
|
|
}
|
|
if _, err := s.submit(ctx, r.Kind, r.ID, "generate", ActionRequest{Version: 1}); !errors.Is(err, ErrForbidden) {
|
|
t.Fatalf("administrator queued another user's task: %v", err)
|
|
}
|
|
r.Name = "Administrator edit"
|
|
if err := s.save(ctx, r, 1); err != nil {
|
|
t.Fatalf("administrator cannot persist tenant resource: %v", err)
|
|
}
|
|
var stored Resource
|
|
if err := s.db.First(&stored, "id = ?", r.ID).Error; err != nil || stored.OwnerID != "contributor" || stored.Name != r.Name || stored.Version != 2 {
|
|
t.Fatalf("administrator edit changed ownership or was not saved: %+v %v", stored, err)
|
|
}
|
|
var revision Revision
|
|
if err := s.db.Where("resource_id = ? AND version = ?", r.ID, 2).First(&revision).Error; err != nil || revision.ActorID != "admin" {
|
|
t.Fatalf("administrator edit audit missing: %+v %v", revision, err)
|
|
}
|
|
var count int64
|
|
s.db.Model(&Job{}).Count(&count)
|
|
if count != 0 {
|
|
t.Fatal("unauthorized job was queued")
|
|
}
|
|
}
|
|
|
|
func TestOnlyAdministratorCanBindAnotherUsersPrivateMap(t *testing.T) {
|
|
s := sqliteWorkbench(t)
|
|
mapResource := seedResource(t, s, 1, "contributor", "training-maps")
|
|
s.db.Model(mapResource).Update("status", "confirmed")
|
|
flow := seedResource(t, s, 1, "admin", "flows")
|
|
ctx := workbenchContext(1, "admin", types.TenantRoleAdmin)
|
|
task := &Resource{TenantID: 1, OwnerID: "admin", Kind: "content-tasks", Data: asJSON(TaskData{MapID: mapResource.ID, FlowID: flow.ID})}
|
|
err := s.db.WithContext(context.Background()).Transaction(func(tx *gorm.DB) error { return lockReferences(ctx, tx, task) })
|
|
if err != nil {
|
|
t.Fatalf("administrator cannot use tenant map: %v", err)
|
|
}
|
|
ctx = workbenchContext(1, "admin", types.TenantRoleContributor)
|
|
err = s.db.Transaction(func(tx *gorm.DB) error { return lockReferences(ctx, tx, task) })
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
t.Fatalf("non-administrator bound another user's private map: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWorkbenchAdministratorNeverEscapesActiveTenant(t *testing.T) {
|
|
s := sqliteWorkbench(t)
|
|
r := seedResource(t, s, 2, "contributor", "flows")
|
|
ctx := context.WithValue(workbenchContext(1, "admin", types.TenantRoleViewer), types.SystemAdminContextKey, true)
|
|
if _, err := lockResource(ctx, s.db, r.Kind, r.ID); !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
t.Fatalf("cross-tenant write lock allowed: %v", err)
|
|
}
|
|
if err := persistResource(ctx, s.db, r, r.Version); !errors.Is(err, ErrForbidden) {
|
|
t.Fatalf("cross-tenant save allowed: %v", err)
|
|
}
|
|
}
|