tinyrelayfile
tinyrelay/internal/daemon/file_metadata_test.go/

internal/daemon/file_metadata_test.go raw
1package daemon
2
3import (
4 "encoding/json"
5 "net/http"
6 "net/http/httptest"
7 "strings"
8 "testing"
9
10 "github.com/FelineStateMachine/tinyrelay/internal/blob"
11)
12
13func TestEncryptedFileMetadataEndpointBindsBodyAndClaim(t *testing.T) {
14 _, tenant := testTenant(t)
15 ctx := t.Context()
16 entry, err := tenant.blobs.Put(ctx, blob.PutOptions{Reader: strings.NewReader("ciphertext"), Type: "application/octet-stream", Uploader: tenant.Policy().Owner})
17 if err != nil {
18 t.Fatal(err)
19 }
20 body := `{"hash":"` + entry.SHA256 + `","name":"Encrypted file ` + entry.SHA256[:12] + `","path":"Encrypted file ` + entry.SHA256[:12] + `","purpose":"file","size":104857600,"type":"application/octet-stream"}`
21 r := httptest.NewRequest(http.MethodPost, "http://relay.test/files/metadata", strings.NewReader(body))
22 r.Header.Set("Content-Type", "application/json")
23 signRequest(t, r, body)
24 w := httptest.NewRecorder()
25 tenant.ServeHTTP(w, r)
26 if w.Code != http.StatusOK {
27 t.Fatalf("metadata status=%d body=%s", w.Code, w.Body.String())
28 }
29 var purpose, typ string
30 var size int64
31 if err := tenant.store.DB().QueryRow("SELECT purpose,logical_size,logical_type FROM blob_claim_metadata WHERE sha256=? AND uploader=?", entry.SHA256, tenant.Policy().Owner).Scan(&purpose, &size, &typ); err != nil {
32 t.Fatal(err)
33 }
34 if purpose != "file" || size != 104857600 || typ != "application/octet-stream" {
35 t.Fatalf("metadata=%q %d %q", purpose, size, typ)
36 }
37 bad := strings.Replace(body, `"size":104857600`, `"size":104857601`, 1)
38 r = httptest.NewRequest(http.MethodPost, "http://relay.test/files/metadata", strings.NewReader(bad))
39 r.Header.Set("Content-Type", "application/json")
40 signRequest(t, r, body)
41 w = httptest.NewRecorder()
42 tenant.ServeHTTP(w, r)
43 if w.Code != http.StatusUnauthorized {
44 t.Fatalf("tampered metadata status=%d body=%s", w.Code, w.Body.String())
45 }
46 unknown := body[:len(body)-1] + `,"key":"must-not-store"}`
47 r = httptest.NewRequest(http.MethodPost, "http://relay.test/files/metadata", strings.NewReader(unknown))
48 r.Header.Set("Content-Type", "application/json")
49 signRequest(t, r, unknown)
50 w = httptest.NewRecorder()
51 tenant.ServeHTTP(w, r)
52 if w.Code != http.StatusBadRequest {
53 t.Fatalf("unknown metadata status=%d body=%s", w.Code, w.Body.String())
54 }
55}
56
57func TestEncryptedUploadFinalizesOneLogicalLibraryFile(t *testing.T) {
58 _, tenant := testTenant(t)
59 body := "an encrypted upload fixture"
60 r := httptest.NewRequest(http.MethodPut, "http://relay.test/upload?purpose=chunk", strings.NewReader(body))
61 r.Header.Set("Content-Type", "application/octet-stream")
62 signRequest(t, r, body)
63 w := httptest.NewRecorder()
64 tenant.ServeHTTP(w, r)
65 if w.Code != http.StatusOK {
66 t.Fatalf("upload: %d %s", w.Code, w.Body.String())
67 }
68 var uploaded struct {
69 Hash string `json:"sha256"`
70 }
71 if err := json.Unmarshal(w.Body.Bytes(), &uploaded); err != nil {
72 t.Fatal(err)
73 }
74 query := []json.RawMessage{json.RawMessage(`{"view":"library"}`)}
75 library, err := tenant.Execute(t.Context(), tenant.Policy().Owner, "browsefiles", query)
76 if err != nil {
77 t.Fatal(err)
78 }
79 if len(fileItems(t, library)) != 0 {
80 t.Fatalf("internal chunk in library: %v", library)
81 }
82 metadata := `{"hash":"` + uploaded.Hash + `","name":"Encrypted file","purpose":"file","size":104857600,"type":"application/octet-stream"}`
83 r = httptest.NewRequest(http.MethodPost, "http://relay.test/files/metadata", strings.NewReader(metadata))
84 signRequest(t, r, metadata)
85 w = httptest.NewRecorder()
86 tenant.ServeHTTP(w, r)
87 if w.Code != http.StatusOK {
88 t.Fatalf("finalize: %d %s", w.Code, w.Body.String())
89 }
90 library, err = tenant.Execute(t.Context(), tenant.Policy().Owner, "browsefiles", query)
91 if err != nil {
92 t.Fatal(err)
93 }
94 items := fileItems(t, library)
95 if len(items) != 1 || items[0]["name"] != "Encrypted file" || items[0]["size"] != int64(104857600) {
96 t.Fatalf("logical file: %v", items)
97 }
98}
99
100func TestFileMetadataCannotChangeAnotherUploadersClaim(t *testing.T) {
101 _, tenant := testTenant(t)
102 entry, err := tenant.blobs.Put(t.Context(), blob.PutOptions{Reader: strings.NewReader("someone else's file"), Uploader: strings.Repeat("b", 64)})
103 if err != nil {
104 t.Fatal(err)
105 }
106 body := `{"hash":"` + entry.SHA256 + `","name":"Changed","purpose":"file"}`
107 r := httptest.NewRequest(http.MethodPost, "http://relay.test/files/metadata", strings.NewReader(body))
108 signRequest(t, r, body)
109 w := httptest.NewRecorder()
110 tenant.ServeHTTP(w, r)
111 if w.Code == http.StatusOK {
112 t.Fatal("owner must not edit another uploader's claim metadata")
113 }
114 var count int
115 if err := tenant.store.DB().QueryRow("SELECT count(*) FROM blob_claim_metadata WHERE sha256=?", entry.SHA256).Scan(&count); err != nil {
116 t.Fatal(err)
117 }
118 if count != 0 {
119 t.Fatal("metadata changed without claim ownership")
120 }
121}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs