clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/standalone-relay
tinyrelay / internal / daemon / private_repository_test.go
private_repository_test.go 5.0K
1package daemon23import (4 "context"5 "encoding/json"6 "strings"7 "testing"8 "time"910 "github.com/FelineStateMachine/tinyrelay/internal/configport"11 "github.com/FelineStateMachine/tinyrelay/internal/event"12 "github.com/FelineStateMachine/tinyrelay/internal/policy"13 "github.com/FelineStateMachine/tinyrelay/internal/relay"14 "github.com/FelineStateMachine/tinyrelay/internal/replication"15 "github.com/FelineStateMachine/tinyrelay/internal/storage"16)1718func TestPrivateRepositoryRejectedOnOpenTenantPublishAndImport(t *testing.T) {19 _, tenant := testTenant(t)20 p := tenant.Policy()21 p.Features.Grasp = true22 p.Reads = "open"23 if err := tenant.applyPolicy(context.Background(), p); err != nil {24 t.Fatal(err)25 }26 secret := strings.Repeat("4", 64)27 owner, err := event.PublicKey(secret)28 if err != nil {29 t.Fatal(err)30 }31 e := event.Event{Kind: event.KIND_REPO, PubKey: owner, CreatedAt: 1, Tags: [][]string{{"d", "private"}, {"private", "true"}, {"clone", "https://relay.example/private.git"}}}32 if err := event.Sign(&e, secret); err != nil {33 t.Fatal(err)34 }35 if _, err := tenant.router.Publish(context.Background(), e, relay.Session{PubKeys: []string{owner}}); err == nil || !strings.Contains(err.Error(), "GRASP-08") {36 t.Fatalf("open publish error = %v", err)37 }38 if err := tenant.ingest(context.Background(), e, replication.OriginImport); err == nil || !strings.Contains(err.Error(), "GRASP-08") {39 t.Fatalf("open import error = %v", err)40 }41}4243func TestPrivateRepositoryPolicyCannotBeReopened(t *testing.T) {44 owner, err := event.PublicKey(strings.Repeat("5", 63) + "1")45 if err != nil {46 t.Fatal(err)47 }48 _, tenant, _ := privatePeerTenant(t, "private-policy", owner)49 e := event.Event{Kind: event.KIND_REPO, PubKey: owner, CreatedAt: 1, Tags: [][]string{{"d", "private"}, {"private", "true"}}}50 if err := event.Sign(&e, strings.Repeat("5", 63)+"1"); err != nil {51 t.Fatal(err)52 }53 if _, err := tenant.store.Save(context.Background(), e, storage.SaveOptions{Now: 1}); err != nil {54 t.Fatal(err)55 }56 p := tenant.Policy()57 p.Features.Grasp08 = false58 p.Reads = "open"59 if err := tenant.applyPolicy(context.Background(), p); err == nil {60 t.Fatal("private policy transition was accepted")61 }62 patch := configport.Config{Format: configport.Format, Policy: map[string]json.RawMessage{63 "features": json.RawMessage(`{"grasp08":false}`), "reads": json.RawMessage(`"open"`),64 }}65 if _, err := tenant.config.Apply(context.Background(), patch, false); err == nil {66 t.Fatal("config import reopened a private tenant")67 }68 var stored policy.Policy69 if err := tenant.store.GetSetting(context.Background(), "policy", &stored); err != nil {70 t.Fatal(err)71 }72 if !tenant.PrivateServiceEnabled() || !stored.Features.Grasp08 || stored.Reads != "members" {73 t.Fatal("rejected import changed the effective or durable private policy")74 }75}7677func TestLegacyPrivateRepositoryForcesBoundaryAfterRestart(t *testing.T) {78 ctx := context.Background()79 dir := t.TempDir()80 app, err := New(ctx, Config{DataDir: dir, DefaultTenant: "main"})81 if err != nil {82 t.Fatal(err)83 }84 secret := strings.Repeat("6", 63) + "1"85 owner, err := event.PublicKey(secret)86 if err != nil {87 t.Fatal(err)88 }89 meta, err := app.Create(ctx, CreateOptions{Name: "main", Owner: owner, Template: "default"})90 if err != nil {91 t.Fatal(err)92 }93 tenant, err := app.tenant(ctx, meta, "http://relay.test")94 if err != nil {95 t.Fatal(err)96 }97 now := time.Now().Unix()98 items := []event.Event{99 {Kind: event.KIND_REPO, PubKey: owner, CreatedAt: now, Tags: [][]string{{"d", "legacy"}, {"private", "true"}}},100 {Kind: event.KIND_REPO_STATE, PubKey: owner, CreatedAt: now + 1, Tags: [][]string{{"d", "legacy"}}},101 {Kind: 1621, PubKey: owner, CreatedAt: now + 2, Tags: [][]string{{"e", "legacy"}}},102 }103 for i := range items {104 if err := event.Sign(&items[i], secret); err != nil {105 t.Fatal(err)106 }107 if _, err := tenant.store.Save(ctx, items[i], storage.SaveOptions{Now: now}); err != nil {108 t.Fatal(err)109 }110 }111 if err := app.Close(ctx); err != nil {112 t.Fatal(err)113 }114 app, err = New(ctx, Config{DataDir: dir, DefaultTenant: "main"})115 if err != nil {116 t.Fatal(err)117 }118 if err := app.Start(ctx, "http://relay.test"); err != nil {119 t.Fatal(err)120 }121 t.Cleanup(func() { _ = app.Close(ctx) })122 reloaded := app.tenants[meta.ID]123 if reloaded == nil || !reloaded.PrivateServiceEnabled() || reloaded.Policy().Reads != "members" {124 t.Fatal("legacy private data did not force members boundary")125 }126 if _, err := reloaded.Query(ctx, []event.Filter{{Kinds: []int{event.KIND_REPO, event.KIND_REPO_STATE, 1621}}}, relay.Session{}); err == nil {127 t.Fatal("anonymous query reached legacy private metadata")128 }129 if _, err := reloaded.Count(ctx, []event.Filter{{Kinds: []int{event.KIND_REPO}}}, relay.Session{}); err == nil {130 t.Fatal("anonymous count reached legacy private metadata")131 }132 var durable policy.Policy133 if err := reloaded.store.GetSetting(ctx, "policy", &durable); err != nil {134 t.Fatal(err)135 }136 if !durable.Features.Grasp08 || durable.Reads != "members" {137 t.Fatal("legacy boundary must remain private after its announcement is removed")138 }139}