tinyrelayfile
tinyrelay/internal/daemon/hosting_routes_acceptance_test.go/

internal/daemon/hosting_routes_acceptance_test.go raw
1package daemon
2
3import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "net/http"
9 "net/http/httptest"
10 "strings"
11 "testing"
12
13 "github.com/FelineStateMachine/tinyrelay/internal/blob"
14 "github.com/FelineStateMachine/tinyrelay/internal/event"
15 "github.com/FelineStateMachine/tinyrelay/internal/relay"
16 "github.com/FelineStateMachine/tinyrelay/internal/sites"
17)
18
19func TestMountedSiteHostIsolatedPerTenant(t *testing.T) {
20 ctx := context.Background()
21 ownerSecret := strings.Repeat("0", 63) + "1"
22 owner, err := event.PublicKey(ownerSecret)
23 if err != nil {
24 t.Fatal(err)
25 }
26 app, err := New(ctx, Config{DataDir: t.TempDir(), DefaultTenant: "alice", PublicURL: "https://relay.test"})
27 if err != nil {
28 t.Fatal(err)
29 }
30 defer app.Close(ctx)
31 var tenants []*Tenant
32 for _, name := range []string{"alice", "bob"} {
33 meta, err := app.Create(ctx, CreateOptions{Name: name, Owner: owner, Template: "default"})
34 if err != nil {
35 t.Fatal(err)
36 }
37 tenant, err := app.tenant(ctx, meta, "https://relay.test")
38 if err != nil {
39 t.Fatal(err)
40 }
41 tenants = append(tenants, tenant)
42 }
43 content := []string{"alice site", "bob site"}
44 label := ""
45 for i, tenant := range tenants {
46 hashBytes := sha256.Sum256([]byte(content[i]))
47 hash := hex.EncodeToString(hashBytes[:])
48 if _, err := tenant.blobs.Put(ctx, blobPut(content[i], owner)); err != nil {
49 t.Fatal(err)
50 }
51 e := event.Event{Kind: sites.KindSite, CreatedAt: 1, Tags: [][]string{{"path", "/index.html", hash}}}
52 if err := event.Sign(&e, ownerSecret); err != nil {
53 t.Fatal(err)
54 }
55 if _, err := tenant.Publish(ctx, e, relay.Session{PubKeys: []string{owner}}); err != nil {
56 t.Fatal(err)
57 }
58 if label == "" {
59 label = sites.SiteLabel(e)
60 }
61 }
62 for i, name := range []string{"alice", "bob"} {
63 req := httptest.NewRequest(http.MethodGet, "https://"+label+"."+name+".relay.test/", nil)
64 res := httptest.NewRecorder()
65 app.ServeHTTP(res, req)
66 if res.Code != http.StatusOK || res.Body.String() != content[i] {
67 t.Fatalf("tenant %s site status=%d body=%q", name, res.Code, res.Body.String())
68 }
69 }
70 // NIP-AD discovery is mounted through the tenant router and must only
71 // reveal an event held by that tenant.
72 note := event.Event{Kind: 1, CreatedAt: 2, Content: "alice-only"}
73 if err := event.Sign(&note, ownerSecret); err != nil {
74 t.Fatal(err)
75 }
76 if _, err := tenants[0].Publish(ctx, note, relay.Session{PubKeys: []string{owner}}); err != nil {
77 t.Fatal(err)
78 }
79 for i, name := range []string{"alice", "bob"} {
80 requestURL := "https://relay.test/.well-known/nostr.json?path=/e/" + note.ID
81 if name == "bob" {
82 requestURL = "https://relay.test/r/bob/.well-known/nostr.json?path=/e/" + note.ID
83 }
84 req := httptest.NewRequest(http.MethodGet, requestURL, nil)
85 signRequest(t, req, "")
86 res := httptest.NewRecorder()
87 app.ServeHTTP(res, req)
88 if res.Code != http.StatusOK {
89 t.Fatalf("NIP-AD %s status=%d body=%s", name, res.Code, res.Body.String())
90 }
91 var doc map[string]struct {
92 Filter map[string]any `json:"filter"`
93 }
94 if err := json.Unmarshal(res.Body.Bytes(), &doc); err != nil {
95 t.Fatal(err)
96 }
97 _, found := doc["/e/"+note.ID]
98 if (i == 0) != found {
99 t.Fatalf("NIP-AD %s found=%v document=%s", name, found, res.Body.String())
100 }
101 if i == 0 && len(doc["/e/"+note.ID].Filter) == 0 {
102 t.Fatal("NIP-AD positive response omitted filter")
103 }
104 }
105 meta, err := app.Catalog().GetByName(ctx, "alice")
106 if err != nil {
107 t.Fatal(err)
108 }
109 if _, err := app.Catalog().AddHost(ctx, meta.ID, "sites.example.test", label); err != nil {
110 t.Fatal(err)
111 }
112 custom := httptest.NewRequest(http.MethodGet, "https://sites.example.test/", nil)
113 customRes := httptest.NewRecorder()
114 app.ServeHTTP(customRes, custom)
115 if customRes.Code != http.StatusOK || customRes.Body.String() != content[0] {
116 t.Fatalf("custom host status=%d body=%q", customRes.Code, customRes.Body.String())
117 }
118 unknown := httptest.NewRequest(http.MethodGet, "https://"+label+".unknown.relay.test/", nil)
119 res := httptest.NewRecorder()
120 app.ServeHTTP(res, unknown)
121 if res.Code == http.StatusOK && strings.Contains(res.Body.String(), "alice site") {
122 t.Fatal("unknown tenant served another tenant site")
123 }
124}
125
126func blobPut(content, uploader string) blob.PutOptions {
127 return blob.PutOptions{Reader: strings.NewReader(content), Type: "text/html", Uploader: uploader}
128}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs