My pastes page /mine with anonymous viewer cookie (#37)
- vwr cookie middleware: random browser id set on first visit (reused by #49) - pastes table gains viewer_id column, set server-side at creation from the cookie - GET /api/mine lists pastes for the requesting browser (title/lang/size/created) - DELETE enforcement: 403 when client-sent vwr doesn't match the paste's viewer_id - /mine page reuses history table styling, delete buttons, empty state - nav: 'Saved' item between Public and Git; Git gets external-link arrow (#56) - tests: create-with-cookie appears in /mine, other cookie doesn't, delete enforcement Closes #37
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// doReq performs a request against the router, carrying the given cookies,
|
||||
// and returns the recorder (so Set-Cookie from the viewer middleware is visible).
|
||||
func doReq(t *testing.T, h http.Handler, method, path, cookie string, body string) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
req := httptest.NewRequest(method, path, strings.NewReader(body))
|
||||
if body != "" {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
if cookie != "" {
|
||||
req.AddCookie(&http.Cookie{Name: "vwr", Value: cookie})
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
return rec
|
||||
}
|
||||
|
||||
// viewerCookieFor performs a request without the vwr cookie and extracts the
|
||||
// one the viewer middleware sets in the response.
|
||||
func viewerCookieFor(t *testing.T, h http.Handler, path string) string {
|
||||
t.Helper()
|
||||
rec := doReq(t, h, "GET", path, "", "")
|
||||
for _, c := range rec.Result().Cookies() {
|
||||
if c.Name == "vwr" {
|
||||
return c.Value
|
||||
}
|
||||
}
|
||||
t.Fatal("vwr cookie not set")
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestMineCreateListDelete(t *testing.T) {
|
||||
globalLimiter = newLimiter() // fresh rate-limit buckets
|
||||
webUI, err := NewWebUI()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
webUIInstance = webUI
|
||||
store, err := OpenStore(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a := &apiServer{store: store, cfg: Config{MaxTextBytes: 5 * 1024 * 1024}}
|
||||
h := a.routes()
|
||||
|
||||
alice := viewerCookieFor(t, h, "/history")
|
||||
if alice == "" {
|
||||
t.Fatal("no viewer cookie issued")
|
||||
}
|
||||
|
||||
// create with alice's cookie -> stored viewer id
|
||||
rec := doReq(t, h, "POST", "/api/pastes", alice, `{"content":"hello mine"}`)
|
||||
if rec.Code != 201 {
|
||||
t.Fatalf("create: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var created struct{ ID string }
|
||||
json.Unmarshal(rec.Body.Bytes(), &created)
|
||||
if created.ID == "" {
|
||||
t.Fatal("no id returned")
|
||||
}
|
||||
|
||||
// owner sees it in /api/mine
|
||||
rec = doReq(t, h, "GET", "/api/mine", alice, "")
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("mine: %d", rec.Code)
|
||||
}
|
||||
var list struct {
|
||||
Total int `json:"total"`
|
||||
Items []struct{ ID string `json:"id"` } `json:"items"`
|
||||
}
|
||||
json.Unmarshal(rec.Body.Bytes(), &list)
|
||||
if list.Total != 1 || len(list.Items) != 1 || list.Items[0].ID != created.ID {
|
||||
t.Fatalf("mine list: total=%d items=%v", list.Total, list.Items)
|
||||
}
|
||||
|
||||
// a different browser's cookie does NOT see it
|
||||
bob := viewerCookieFor(t, h, "/history")
|
||||
rec = doReq(t, h, "GET", "/api/mine", bob, "")
|
||||
json.Unmarshal(rec.Body.Bytes(), &list)
|
||||
if list.Total != 0 {
|
||||
t.Fatalf("other browser sees %d pastes, want 0", list.Total)
|
||||
}
|
||||
|
||||
// delete enforcement: bob cannot delete alice's paste
|
||||
rec = doReq(t, h, "DELETE", "/api/pastes/"+created.ID, bob, "")
|
||||
if rec.Code != 403 {
|
||||
t.Fatalf("bob delete: %d, want 403", rec.Code)
|
||||
}
|
||||
|
||||
// owner can delete
|
||||
rec = doReq(t, h, "DELETE", "/api/pastes/"+created.ID, alice, "")
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("alice delete: %d", rec.Code)
|
||||
}
|
||||
rec = doReq(t, h, "GET", "/api/mine", alice, "")
|
||||
json.Unmarshal(rec.Body.Bytes(), &list)
|
||||
if list.Total != 0 {
|
||||
t.Fatalf("after delete, mine total=%d, want 0", list.Total)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user