mirror of
https://github.com/Dvorinka/excalidraw-full.git
synced 2026-06-03 22:02:57 +00:00
221 lines
7.6 KiB
Go
221 lines
7.6 KiB
Go
package workspace
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
func TestInviteAcceptAddsEditorMembership(t *testing.T) {
|
|
api, cleanup := newTestAPI(t)
|
|
defer cleanup()
|
|
|
|
aliceCookie, _, aliceTeam := signup(t, api, "alice@example.com")
|
|
bobCookie, _, _ := signup(t, api, "bob@example.com")
|
|
|
|
inviteRR := doJSON(t, api, http.MethodPost, "/teams/"+aliceTeam.ID+"/invites", map[string]any{
|
|
"email": "bob@example.com",
|
|
"role": "editor",
|
|
}, aliceCookie)
|
|
if inviteRR.Code != http.StatusCreated {
|
|
t.Fatalf("invite status = %d body = %s", inviteRR.Code, inviteRR.Body.String())
|
|
}
|
|
var invite struct {
|
|
Invite TeamInvite `json:"invite"`
|
|
Token string `json:"token"`
|
|
}
|
|
if err := json.Unmarshal(inviteRR.Body.Bytes(), &invite); err != nil {
|
|
t.Fatalf("invite decode error = %v", err)
|
|
}
|
|
if invite.Token == "" || invite.Invite.Email != "bob@example.com" {
|
|
t.Fatalf("unexpected invite: %#v", invite)
|
|
}
|
|
|
|
beforeRR := doJSON(t, api, http.MethodGet, "/teams/"+aliceTeam.ID+"/members", nil, bobCookie)
|
|
if beforeRR.Code != http.StatusForbidden {
|
|
t.Fatalf("bob members before accept status = %d body = %s", beforeRR.Code, beforeRR.Body.String())
|
|
}
|
|
|
|
acceptRR := doJSON(t, api, http.MethodPost, "/invites/accept", map[string]string{"token": invite.Token}, bobCookie)
|
|
if acceptRR.Code != http.StatusOK {
|
|
t.Fatalf("accept status = %d body = %s", acceptRR.Code, acceptRR.Body.String())
|
|
}
|
|
|
|
afterRR := doJSON(t, api, http.MethodGet, "/teams/"+aliceTeam.ID+"/members", nil, bobCookie)
|
|
if afterRR.Code != http.StatusOK {
|
|
t.Fatalf("bob members after accept status = %d body = %s", afterRR.Code, afterRR.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestRestrictedDrawingGrantAllowsViewNotEdit(t *testing.T) {
|
|
api, cleanup := newTestAPI(t)
|
|
defer cleanup()
|
|
|
|
aliceCookie, _, aliceTeam := signup(t, api, "alice@example.com")
|
|
bobCookie, _, _ := signup(t, api, "bob@example.com")
|
|
|
|
createRR := doJSON(t, api, http.MethodPost, "/drawings", map[string]any{
|
|
"team_id": aliceTeam.ID,
|
|
"title": "Restricted map",
|
|
"visibility": "restricted",
|
|
}, aliceCookie)
|
|
if createRR.Code != http.StatusCreated {
|
|
t.Fatalf("create drawing status = %d body = %s", createRR.Code, createRR.Body.String())
|
|
}
|
|
var drawing Drawing
|
|
if err := json.Unmarshal(createRR.Body.Bytes(), &drawing); err != nil {
|
|
t.Fatalf("drawing decode error = %v", err)
|
|
}
|
|
|
|
noAccessRR := doJSON(t, api, http.MethodGet, "/drawings/"+drawing.ID, nil, bobCookie)
|
|
if noAccessRR.Code != http.StatusForbidden {
|
|
t.Fatalf("bob get before grant status = %d body = %s", noAccessRR.Code, noAccessRR.Body.String())
|
|
}
|
|
|
|
grantRR := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/permissions", map[string]any{
|
|
"subject_type": "user",
|
|
"email": "bob@example.com",
|
|
"permission": "view",
|
|
}, aliceCookie)
|
|
if grantRR.Code != http.StatusCreated {
|
|
t.Fatalf("grant status = %d body = %s", grantRR.Code, grantRR.Body.String())
|
|
}
|
|
|
|
getRR := doJSON(t, api, http.MethodGet, "/drawings/"+drawing.ID, nil, bobCookie)
|
|
if getRR.Code != http.StatusOK {
|
|
t.Fatalf("bob get after grant status = %d body = %s", getRR.Code, getRR.Body.String())
|
|
}
|
|
|
|
editRR := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/revisions", map[string]any{
|
|
"snapshot": map[string]any{"type": "excalidraw", "elements": []any{}},
|
|
}, bobCookie)
|
|
if editRR.Code != http.StatusForbidden {
|
|
t.Fatalf("bob edit with view grant status = %d body = %s", editRR.Code, editRR.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestShareLinkAllowsUnauthenticatedDrawingRead(t *testing.T) {
|
|
api, cleanup := newTestAPI(t)
|
|
defer cleanup()
|
|
|
|
aliceCookie, _, aliceTeam := signup(t, api, "alice@example.com")
|
|
createRR := doJSON(t, api, http.MethodPost, "/drawings", map[string]any{
|
|
"team_id": aliceTeam.ID,
|
|
"title": "Shared map",
|
|
}, aliceCookie)
|
|
if createRR.Code != http.StatusCreated {
|
|
t.Fatalf("create drawing status = %d body = %s", createRR.Code, createRR.Body.String())
|
|
}
|
|
var drawing Drawing
|
|
if err := json.Unmarshal(createRR.Body.Bytes(), &drawing); err != nil {
|
|
t.Fatalf("drawing decode error = %v", err)
|
|
}
|
|
|
|
shareRR := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/share-links", map[string]any{
|
|
"permission": "view",
|
|
}, aliceCookie)
|
|
if shareRR.Code != http.StatusCreated {
|
|
t.Fatalf("share status = %d body = %s", shareRR.Code, shareRR.Body.String())
|
|
}
|
|
var share struct {
|
|
ShareLink ShareLink `json:"share_link"`
|
|
Token string `json:"token"`
|
|
}
|
|
if err := json.Unmarshal(shareRR.Body.Bytes(), &share); err != nil {
|
|
t.Fatalf("share decode error = %v", err)
|
|
}
|
|
if share.Token == "" || share.ShareLink.TokenHash != "" {
|
|
t.Fatalf("unexpected share response: %#v", share)
|
|
}
|
|
|
|
publicRR := doJSON(t, api, http.MethodGet, "/shared/"+share.Token, nil)
|
|
if publicRR.Code != http.StatusOK {
|
|
t.Fatalf("public shared status = %d body = %s", publicRR.Code, publicRR.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestEmbedsRejectUnsafeURLs(t *testing.T) {
|
|
api, cleanup := newTestAPI(t)
|
|
defer cleanup()
|
|
|
|
cookie, _, team := signup(t, api, "alice@example.com")
|
|
createRR := doJSON(t, api, http.MethodPost, "/drawings", map[string]any{
|
|
"team_id": team.ID,
|
|
"title": "Embed map",
|
|
}, cookie)
|
|
if createRR.Code != http.StatusCreated {
|
|
t.Fatalf("create drawing status = %d body = %s", createRR.Code, createRR.Body.String())
|
|
}
|
|
var drawing Drawing
|
|
if err := json.Unmarshal(createRR.Body.Bytes(), &drawing); err != nil {
|
|
t.Fatalf("drawing decode error = %v", err)
|
|
}
|
|
|
|
for _, unsafeURL := range []string{"javascript:alert(1)", "http://127.0.0.1/admin", "http://localhost:3002"} {
|
|
rr := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/embeds", map[string]any{
|
|
"source_url": unsafeURL,
|
|
"embed_type": "link",
|
|
}, cookie)
|
|
if rr.Code != http.StatusBadRequest {
|
|
t.Fatalf("unsafe url %q status = %d body = %s", unsafeURL, rr.Code, rr.Body.String())
|
|
}
|
|
}
|
|
|
|
safeRR := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/embeds", map[string]any{
|
|
"source_url": "https://example.com/roadmap",
|
|
"embed_type": "link",
|
|
}, cookie)
|
|
if safeRR.Code != http.StatusCreated {
|
|
t.Fatalf("safe embed status = %d body = %s", safeRR.Code, safeRR.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestAssetsAndLinkReferences(t *testing.T) {
|
|
api, cleanup := newTestAPI(t)
|
|
defer cleanup()
|
|
|
|
cookie, _, team := signup(t, api, "alice@example.com")
|
|
projectRR := doJSON(t, api, http.MethodPost, "/projects", map[string]any{
|
|
"team_id": team.ID,
|
|
"name": "Roadmap",
|
|
}, cookie)
|
|
if projectRR.Code != http.StatusCreated {
|
|
t.Fatalf("project status = %d body = %s", projectRR.Code, projectRR.Body.String())
|
|
}
|
|
var project Project
|
|
if err := json.Unmarshal(projectRR.Body.Bytes(), &project); err != nil {
|
|
t.Fatalf("project decode error = %v", err)
|
|
}
|
|
drawingRR := doJSON(t, api, http.MethodPost, "/drawings", map[string]any{
|
|
"team_id": team.ID,
|
|
"title": "Linked map",
|
|
}, cookie)
|
|
if drawingRR.Code != http.StatusCreated {
|
|
t.Fatalf("drawing status = %d body = %s", drawingRR.Code, drawingRR.Body.String())
|
|
}
|
|
var drawing Drawing
|
|
if err := json.Unmarshal(drawingRR.Body.Bytes(), &drawing); err != nil {
|
|
t.Fatalf("drawing decode error = %v", err)
|
|
}
|
|
|
|
assetRR := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/assets", map[string]any{
|
|
"kind": "attachment",
|
|
"mime_type": "image/png",
|
|
"size": 2048,
|
|
"width": 800,
|
|
"height": 600,
|
|
}, cookie)
|
|
if assetRR.Code != http.StatusCreated {
|
|
t.Fatalf("asset status = %d body = %s", assetRR.Code, assetRR.Body.String())
|
|
}
|
|
|
|
linkRR := doJSON(t, api, http.MethodPost, "/drawings/"+drawing.ID+"/links", map[string]any{
|
|
"target_resource_type": "project",
|
|
"target_resource_id": project.ID,
|
|
"label": "Roadmap project",
|
|
}, cookie)
|
|
if linkRR.Code != http.StatusCreated {
|
|
t.Fatalf("link status = %d body = %s", linkRR.Code, linkRR.Body.String())
|
|
}
|
|
}
|