oficina / memoria Public

Clone
git clone https://amadan.net/oficina/memoria

Plain git — no account needed to clone.

Download

Download this file

1package memoria
2
3import (
4 "bytes"
5 "database/sql"
6 "errors"
7 "testing"
8
9 "amadan.net/rastrillo/rastrillo/migrate"
10 "amadan.net/rastrillo/rastrillo/sessions"
11)
12
13func homeIdentityFixture(t *testing.T) *app {
14 t.Helper()
15 store := storeForTest(t)
16 if _, err := migrate.Apply(t.Context(), store.db, homeIdentitySchema); err != nil {
17 t.Fatal(err)
18 }
19 return &app{db: store.db, store: store, owners: []string{"alice@example.test", "bob@example.test"}}
20}
21
22func TestHomeLocalIdentityRequiresIndependentAdmittedLocalSession(t *testing.T) {
23 a := homeIdentityFixture(t)
24 for _, session := range []sessions.Session{
25 {Subject: "alice@example.test", Method: "home-assertion"},
26 {Subject: "alice@example.test", Method: ""},
27 {Subject: "stranger@example.test", Method: "magiclink"},
28 {Subject: "", Method: "keymail"},
29 } {
30 if _, _, err := a.ensureHomeLocalIdentity(t.Context(), session); err == nil {
31 t.Fatal("unverified or unadmitted identity allocated a binding")
32 }
33 }
34 var count int
35 if err := a.db.Writer().QueryRow("SELECT COUNT(*) FROM memoria_home_local_identities").Scan(&count); err != nil || count != 0 {
36 t.Fatal("refusal created a binding", count, err)
37 }
38 first, label, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "Alice@Example.Test", Method: "magiclink"})
39 if err != nil || first <= 0 || label != "alice@example.test" {
40 t.Fatal(first, label, err)
41 }
42 again, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "alice@example.test", Method: "keymail"})
43 if err != nil || again != first {
44 t.Fatal("local account binding changed with method", again, err)
45 }
46 var sealed []byte
47 var lookup string
48 if err := a.db.Writer().QueryRow("SELECT address_lookup,address_sealed FROM memoria_home_local_identities WHERE id=?", first).Scan(&lookup, &sealed); err != nil {
49 t.Fatal(err)
50 }
51 if bytes.Contains(sealed, []byte(label)) || bytes.Contains([]byte(lookup), []byte(label)) {
52 t.Fatal("binding persisted a readable address")
53 }
54 a.owners = []string{"bob@example.test"}
55 if _, err := a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), first); err == nil {
56 t.Fatal("removed owner retained Home admission")
57 }
58}
59
60func TestHomeLocalIdentityCiphertextIsBoundToRowAndLookup(t *testing.T) {
61 a := homeIdentityFixture(t)
62 first, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "alice@example.test", Method: "magiclink"})
63 if err != nil {
64 t.Fatal(err)
65 }
66 second, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "bob@example.test", Method: "magiclink"})
67 if err != nil || second == first {
68 t.Fatal(second, err)
69 }
70 if _, err := a.db.Writer().Exec("UPDATE memoria_home_local_identities SET address_sealed=(SELECT address_sealed FROM memoria_home_local_identities WHERE id=?) WHERE id=?", second, first); err != nil {
71 t.Fatal(err)
72 }
73 if _, err := a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), first); err == nil {
74 t.Fatal("moved ciphertext changed the account identity")
75 }
76 if _, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "alice@example.test", Method: "magiclink"}); err == nil {
77 t.Fatal("local sign-in repaired a corrupt binding silently")
78 }
79}
80
81func TestHomeLocalSessionLookupNeverAllocates(t *testing.T) {
82 a := homeIdentityFixture(t)
83 session := sessions.Session{Subject: "alice@example.test", Method: "magiclink"}
84 if _, _, err := a.localHomeIdentity(t.Context(), session); err == nil {
85 t.Fatal("unbound local identity resolved")
86 }
87 var n int
88 if err := a.db.Writer().QueryRow("SELECT COUNT(*) FROM memoria_home_local_identities").Scan(&n); err != nil || n != 0 {
89 t.Fatal("lookup allocated identity", n, err)
90 }
91 id, _, err := a.ensureHomeLocalIdentity(t.Context(), session)
92 if err != nil {
93 t.Fatal(err)
94 }
95 got, label, err := a.localHomeIdentity(t.Context(), session)
96 if err != nil || got != id || label != session.Subject {
97 t.Fatal("bound identity failed", got, label, err)
98 }
99}
100
101func TestHomeLocalIdentityRejectsWholeRowReassignment(t *testing.T) {
102 a := homeIdentityFixture(t)
103 first, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "alice@example.test", Method: "magiclink"})
104 if err != nil {
105 t.Fatal(err)
106 }
107 second, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "bob@example.test", Method: "magiclink"})
108 if err != nil {
109 t.Fatal(err)
110 }
111 tx, err := a.db.Writer().Begin()
112 if err != nil {
113 t.Fatal(err)
114 }
115 defer tx.Rollback()
116 for _, pair := range [][2]int64{{first, -1}, {second, first}, {-1, second}} {
117 if _, err = tx.Exec("UPDATE memoria_home_local_identities SET id=? WHERE id=?", pair[1], pair[0]); err != nil {
118 t.Fatal(err)
119 }
120 }
121 if err = tx.Commit(); err != nil {
122 t.Fatal(err)
123 }
124 if _, err = a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), first); err == nil {
125 t.Fatal("moving ciphertext and its lookup together reassigned a linked identity")
126 }
127}
128
129func TestHomeLocalIdentityIDIsNotReusedAfterDeletion(t *testing.T) {
130 a := homeIdentityFixture(t)
131 s := sessions.Session{Subject: "alice@example.test", Method: "magiclink"}
132 first, _, err := a.ensureHomeLocalIdentity(t.Context(), s)
133 if err != nil {
134 t.Fatal(err)
135 }
136 if _, err = a.db.Writer().Exec("DELETE FROM memoria_home_local_identities WHERE id=?", first); err != nil {
137 t.Fatal(err)
138 }
139 if _, err = a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), first); err == nil {
140 t.Fatal("deleted identity still resolved")
141 }
142 next, _, err := a.ensureHomeLocalIdentity(t.Context(), s)
143 if err != nil || next <= first {
144 t.Fatal("deleted identity ID reused", first, next, err)
145 }
146}
147
148func TestHomeLookupDistinguishesMissingIdentityFromStorageFailure(t *testing.T) {
149 a := homeIdentityFixture(t)
150 _, err := a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), 999)
151 if !errors.Is(err, sql.ErrNoRows) {
152 t.Fatalf("missing identity: %v", err)
153 }
154 id, _, err := a.ensureHomeLocalIdentity(t.Context(), sessions.Session{Subject: "alice@example.test", Method: "magiclink"})
155 if err != nil {
156 t.Fatal(err)
157 }
158 a.SetOwners([]string{"bob@example.test"})
159 if _, err = a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), id); !errors.Is(err, sql.ErrNoRows) {
160 t.Fatalf("removed admission: %v", err)
161 }
162 if _, err = a.db.Writer().Exec("ALTER TABLE memoria_home_local_identities RENAME TO unavailable_home_identities"); err != nil {
163 t.Fatal(err)
164 }
165 if _, err = a.lookupHomeLocalIdentity(t.Context(), a.db.Writer(), id); err == nil || errors.Is(err, sql.ErrNoRows) {
166 t.Fatalf("storage failure became missing: %v", err)
167 }
168}
169