feat(server): add project sharing access control
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"img_infinite_canvas/internal/domain/design"
|
||||
sharingmodule "img_infinite_canvas/internal/modules/sharing"
|
||||
"img_infinite_canvas/internal/svc"
|
||||
"img_infinite_canvas/internal/types"
|
||||
)
|
||||
|
||||
func TestFilterSharedProjectRedactsCanvasOnlyData(t *testing.T) {
|
||||
project := design.Project{
|
||||
ID: "project-1",
|
||||
UserID: "91cd197b-8255-4172-9981-77391fd38f33",
|
||||
Brief: "private brief",
|
||||
LastThreadID: "thread-1",
|
||||
Messages: []design.Message{{
|
||||
ID: "message-1",
|
||||
Content: "private chat",
|
||||
CreatedAt: time.Now(),
|
||||
}},
|
||||
}
|
||||
canvasAccess := sharingmodule.Access{
|
||||
Permission: sharingmodule.PermissionCanvas,
|
||||
Capabilities: sharingmodule.PermissionCanvas.Capabilities(),
|
||||
}
|
||||
|
||||
filtered := filterSharedProject(project, canvasAccess)
|
||||
if filtered.UserID != "" || filtered.Brief != "" || filtered.LastThreadID != "" || len(filtered.Messages) != 0 {
|
||||
t.Fatalf("canvas-only response leaked private fields: %+v", filtered)
|
||||
}
|
||||
if project.UserID == "" || len(project.Messages) == 0 {
|
||||
t.Fatal("filter must not mutate the source project")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitGeneratorTaskRejectsShareProjectMismatch(t *testing.T) {
|
||||
ctx := sharingmodule.ContextWithAccess(context.Background(), sharingmodule.Access{ProjectID: "shared-project"})
|
||||
logic := NewSubmitGeneratorTaskLogic(ctx, &svc.ServiceContext{})
|
||||
_, err := logic.SubmitGeneratorTask(&types.GeneratorTaskSubmitRequest{ProjectId: "other-project"})
|
||||
if !errors.Is(err, sharingmodule.ErrNotFound) {
|
||||
t.Fatalf("expected share project mismatch to be rejected, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterSharedProjectKeepsViewerMessages(t *testing.T) {
|
||||
project := design.Project{
|
||||
UserID: "91cd197b-8255-4172-9981-77391fd38f33",
|
||||
Messages: []design.Message{{
|
||||
ID: "message-1",
|
||||
Content: "visible chat",
|
||||
}},
|
||||
}
|
||||
viewerAccess := sharingmodule.Access{
|
||||
Permission: sharingmodule.PermissionViewer,
|
||||
Capabilities: sharingmodule.PermissionViewer.Capabilities(),
|
||||
}
|
||||
|
||||
filtered := filterSharedProject(project, viewerAccess)
|
||||
if filtered.UserID != "" {
|
||||
t.Fatal("shared response must not expose the owner id")
|
||||
}
|
||||
if len(filtered.Messages) != 1 || filtered.Messages[0].Content != "visible chat" {
|
||||
t.Fatalf("viewer should retain chat messages: %+v", filtered.Messages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterShareMembersRemovesOwnerIdentifiers(t *testing.T) {
|
||||
owner := sharingmodule.Actor{
|
||||
Authenticated: true,
|
||||
UserID: "91cd197b-8255-4172-9981-77391fd38f33",
|
||||
Email: "owner@example.com",
|
||||
CountryCode: "+86",
|
||||
Phone: "13800138000",
|
||||
}
|
||||
members := []sharingmodule.Member{
|
||||
{ID: "email", Identifier: "OWNER@EXAMPLE.COM", Permission: sharingmodule.PermissionEditor},
|
||||
{ID: "phone", Identifier: "+86 138-0013-8000", Permission: sharingmodule.PermissionViewer},
|
||||
{ID: "other", Identifier: "member@example.com", Permission: sharingmodule.PermissionViewer},
|
||||
}
|
||||
|
||||
filtered := filterShareMembers(members, owner)
|
||||
if len(filtered) != 1 || filtered[0].ID != "other" {
|
||||
t.Fatalf("expected only the non-owner member, got %+v", filtered)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user