package app import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" ) func loginTestAdmin(t *testing.T, c *testClient) { t.Helper() expect(t, c.json(t, "POST", "/admin/login", map[string]string{"username": "admin", "password": "test-password"}), 200) } func TestAdminRuntimeBudgetsPersistAndProtectExistingFiles(t *testing.T) { c := fixture(t, false) expect(t, c.request(t, "GET", "/admin/runtime", nil), 401) data := []byte("preserved file") id, files := c.create(t, []string{"a"}, [][]byte{data}, 3, false) c.upload(t, id, files[0], data) code := c.publish(t, id) loginTestAdmin(t, c) settings, e := c.a.runtimeSettings() if e != nil { t.Fatal(e) } settings.FileBudget = 1 expect(t, c.json(t, "PUT", "/admin/runtime", settings), 200) expect(t, c.json(t, "POST", "/uploads", Manifest{Files: []ManifestFile{{Name: "new", Size: 1}}}), 507) c.resolve(t, code) expect(t, c.request(t, "GET", "/pickup/files/"+files[0]+"/download", nil), 200) cfg := c.a.cfg c.a.Close() c.a.db = nil c.a.root = nil c.a.lock = nil a, e := Open(cfg) if e != nil { t.Fatal(e) } defer a.Close() saved, e := a.runtimeSettings() if e != nil || saved.FileBudget != 1 { t.Fatal("runtime budget reverted on restart") } if scalar(t, a, "SELECT count(*) FROM objects") != 1 { t.Fatal("reducing quota deleted existing files") } } func TestAuditRetentionRecordsAndUTF8ByteBudget(t *testing.T) { c := fixture(t, false) settings, e := c.a.runtimeSettings() if e != nil { t.Fatal(e) } settings.AuditRetentionDays = 2 settings.AuditMaxRecords = 3 settings.AuditMaxBytes = 1024 b, _ := json.Marshal(settings) c.a.db.Exec("UPDATE runtime_settings SET json=? WHERE id=1", string(b)) _, e = c.a.db.Exec("INSERT INTO audit(created,action,target,source) VALUES(?,?,?,?)", c.a.now().Unix()-3*86400, "old", "", "") if e != nil { t.Fatal(e) } for i := 0; i < 8; i++ { c.a.audit("new", "target", "203.0.113.1") } if scalar(t, c.a, "SELECT count(*) FROM audit") != 3 || scalar(t, c.a, "SELECT count(*) FROM audit WHERE action='old'") != 0 { t.Fatal("retention/count limits failed") } settings.AuditMaxRecords = 100 b, _ = json.Marshal(settings) c.a.db.Exec("UPDATE runtime_settings SET json=? WHERE id=1", string(b)) for i := 0; i < 10; i++ { c.a.audit("utf8", strings.Repeat("鸟", 60), "source") } actual := scalar(t, c.a, "SELECT COALESCE(sum("+auditRecordBytes+"),0) FROM audit") ledger := scalar(t, c.a, "SELECT content_bytes FROM audit_usage WHERE id=1") if actual != ledger || actual > 1024 { t.Fatalf("audit bytes actual=%d ledger=%d", actual, ledger) } if scalar(t, c.a, "SELECT records FROM audit_usage WHERE id=1") != scalar(t, c.a, "SELECT count(*) FROM audit") { t.Fatal("audit count ledger incorrect") } _, e = c.a.db.Exec("UPDATE audit SET created=?", c.a.now().Unix()-3*86400) if e != nil { t.Fatal(e) } if e = c.a.Cleanup(); e != nil { t.Fatal(e) } if scalar(t, c.a, "SELECT count(*) FROM audit") != 0 { t.Fatal("background cleanup did not age audit events") } } func TestAnonymousIdentityAndReceiptHaveIndependentLifetimes(t *testing.T) { c := fixture(t, false) base := time.Now() c.a.now = func() time.Time { return base } var info struct { Expires int64 `json:"visitor_expires"` } w := c.request(t, "GET", "/policy", nil) expect(t, w, 200) json.Unmarshal(w.Body.Bytes(), &info) if info.Expires != base.Unix()+7*86400 { t.Fatal("cookie lifetime not seven days") } w = c.json(t, "POST", "/uploads", Manifest{Files: []ManifestFile{{Name: "a", Size: 3}}, Days: 7, Downloads: 5, Renew: true}) expect(t, w, 201) var receipt struct { ID string `json:"id"` Token string `json:"manage_token"` Files []struct { ID string `json:"id"` } `json:"files"` } json.Unmarshal(w.Body.Bytes(), &receipt) c.upload(t, receipt.ID, receipt.Files[0].ID, []byte("abc")) code := c.publish(t, receipt.ID) c.a.now = func() time.Time { return base.Add(6 * 24 * time.Hour) } w = c.request(t, "GET", "/policy", nil) json.Unmarshal(w.Body.Bytes(), &info) if info.Expires != base.Unix()+7*86400 { t.Fatal("fixed visitor lifetime unexpectedly extended") } c.resolve(t, code) expect(t, c.request(t, "GET", "/pickup/files/"+receipt.Files[0].ID+"/download", nil), 200) c.a.now = func() time.Time { return base.Add(8 * 24 * time.Hour) } expect(t, c.request(t, "GET", "/policy", nil), 200) w = c.request(t, "GET", "/mine", nil) expect(t, w, 200) if strings.TrimSpace(w.Body.String()) != "[]" { t.Fatal("expired browser identity still owns old list") } r := c.makeReceiptRequest("GET", "/uploads/"+receipt.ID, receipt.Token, nil) response := serveTest(c, r) expect(t, response, 200) c.a.now = func() time.Time { return base.Add(31 * 24 * time.Hour) } if e := c.a.Cleanup(); e != nil { t.Fatal(e) } expect(t, serveTest(c, c.makeReceiptRequest("GET", "/uploads/"+receipt.ID, receipt.Token, nil)), 404) } func TestServiceSwitchesDoNotGiveAdminUploadOrPickupBypass(t *testing.T) { c := fixture(t, false) data := []byte("content") id, files := c.create(t, []string{"a"}, [][]byte{data}, 3, false) c.upload(t, id, files[0], data) c.resolve(t, c.publish(t, id)) loginTestAdmin(t, c) policy, e := c.a.policy() if e != nil { t.Fatal(e) } policy.AnonymousUpload = false policy.AnonymousDownload = false expect(t, c.json(t, "PUT", "/admin/policy", policy), 200) expect(t, c.json(t, "POST", "/uploads", Manifest{Files: []ManifestFile{{Name: "a", Size: 1}}}), 403) expect(t, c.request(t, "GET", "/pickup/files/"+files[0]+"/download", nil), 403) expect(t, c.request(t, "GET", "/admin/stats", nil), 200) } func (c *testClient) makeReceiptRequest(method, path, token string, body []byte) *http.Request { r := httptest.NewRequest(method, "http://localhost:8080/api/v1"+path, bytes.NewReader(body)) r.RemoteAddr = c.ip + ":80" r.Header.Set("Origin", "http://localhost:8080") r.Header.Set("Authorization", "Bearer "+token) return r } func serveTest(c *testClient, r *http.Request) *httptest.ResponseRecorder { w := httptest.NewRecorder() c.h.ServeHTTP(w, r) return w }