oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1package principalstore_test
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "path/filepath"
8 "sync"
9 "testing"
10
11 "amadan.net/rastrillo/rastrillo/db"
12 "amadan.net/rastrillo/rastrillo/migrate"
13 "github.com/carlosframework/docs/internal/docs"
14 "github.com/carlosframework/docs/internal/docs/linkstore"
15 p "github.com/carlosframework/docs/internal/docs/principalstore"
16)
17
18const issuer = "https://id.oficina.works"
19
20func fixture(t *testing.T, apply bool) (*db.DB, *p.Store) {
21 t.Helper()
22 d, e := db.Open(filepath.Join(t.TempDir(), "docs.db"), nil)
23 if e != nil {
24 t.Fatal(e)
25 }
26 t.Cleanup(func() { d.Close() })
27 if _, e = migrate.Apply(context.Background(), d, migrate.Merge(docs.Schema, linkstore.Schema)); e != nil {
28 t.Fatal(e)
29 }
30 if apply {
31 if _, e = migrate.Apply(context.Background(), d, p.Schema); e != nil {
32 t.Fatal(e)
33 }
34 }
35 s, e := p.New(d.Writer(), p.Config{Issuer: issuer, Team: "A1", RequireMembership: func(_ context.Context, r p.MembershipRequest) error {
36 if r.Issuer != issuer || r.Team != "A1" || r.Subject == "absent" {
37 return p.ErrRefused
38 }
39 return nil
40 }})
41 if e != nil {
42 t.Fatal(e)
43 }
44 return d, s
45}
46func exec(t *testing.T, d *db.DB, q string, args ...any) {
47 t.Helper()
48 if _, e := d.Writer().Exec(q, args...); e != nil {
49 t.Fatal(e)
50 }
51}
52func local(t *testing.T, d *db.DB, address string) int64 {
53 t.Helper()
54 r, e := d.Writer().Exec(`INSERT INTO people(email,created_at)VALUES(?,CURRENT_TIMESTAMP)`, address)
55 if e != nil {
56 t.Fatal(e)
57 }
58 id, e := r.LastInsertId()
59 if e != nil {
60 t.Fatal(e)
61 }
62 return id
63}
64func TestAllocationIsolationAddressAbsenceAndNoSamples(t *testing.T) {
65 d, s := fixture(t, false)
66 a := local(t, d, "alice@example.invalid")
67 exec(t, d, `INSERT INTO docs_identity_links VALUES(?,?,?)`, issuer, "alice", a)
68 if _, e := migrate.Apply(context.Background(), d, p.Schema); e != nil {
69 t.Fatal(e)
70 }
71 old, e := s.Resolve(context.Background(), issuer, "alice")
72 if e != nil || old.ID != a || old.Kind != p.Local || old.VerifiedAddress == nil || *old.VerifiedAddress != "alice@example.invalid" {
73 t.Fatal(old, e)
74 }
75 h, e := s.EnsureHome(context.Background(), p.HomeIdentity{Issuer: issuer, Subject: "new-home"})
76 if e != nil || h.ID != p.HomeIDMin || h.Kind != p.Home || h.VerifiedAddress != nil {
77 t.Fatal(h, e)
78 }
79 b := local(t, d, "bob@example.invalid")
80 if b != a+1 {
81 t.Fatal("trigger changed LastInsertId", a, b)
82 }
83 got, e := s.Person(context.Background(), b)
84 if e != nil || got.ID != b || got.Kind != p.Local {
85 t.Fatal(got, e)
86 }
87 resolved, e := s.Resolve(context.Background(), issuer, "new-home")
88 if e != nil || resolved.ID != h.ID || resolved.VerifiedAddress != nil {
89 t.Fatal(resolved, e)
90 }
91 for _, table := range []string{"documents", "grants"} {
92 var n int
93 if e = d.Writer().QueryRow("SELECT COUNT(*) FROM " + table).Scan(&n); e != nil || n != 0 {
94 t.Fatal(table, n, e)
95 }
96 }
97 if _, e = s.EnsureHome(context.Background(), p.HomeIdentity{Issuer: issuer, Subject: "alice"}); !errors.Is(e, p.ErrRefused) {
98 t.Fatal("merged local account", e)
99 }
100 if _, e = d.Writer().Exec(`INSERT INTO docs_identity_links VALUES(?,?,?)`, issuer, "new-home", b); e == nil {
101 t.Fatal("cross-table collision accepted")
102 }
103}
104func TestMembershipSeamAndIdentityOnlyResolve(t *testing.T) {
105 d, s := fixture(t, true)
106 ctx := context.Background()
107 for _, failure := range []error{p.ErrRefused, p.ErrUnavailable, errors.New("secret diagnostic")} {
108 cfg := p.Config{Issuer: issuer, Team: "A1", RequireMembership: func(context.Context, p.MembershipRequest) error { return failure }}
109 blocked, e := p.New(d.Writer(), cfg)
110 if e != nil {
111 t.Fatal(e)
112 }
113 _, e = blocked.EnsureHome(ctx, p.HomeIdentity{Issuer: issuer, Subject: "new"})
114 want := p.ErrUnavailable
115 if failure == p.ErrRefused {
116 want = p.ErrRefused
117 }
118 if e != want {
119 t.Fatal(e)
120 }
121 }
122 none, _ := p.New(d.Writer(), p.Config{Issuer: issuer, Team: "A1"})
123 if _, e := none.EnsureHome(ctx, p.HomeIdentity{issuer, "new"}); e != p.ErrRefused {
124 t.Fatal(e)
125 }
126 var n int
127 d.Writer().QueryRow(`SELECT COUNT(*) FROM docs_principals`).Scan(&n)
128 if n != 0 {
129 t.Fatal("unapproved principal", n)
130 }
131 h, e := s.EnsureHome(ctx, p.HomeIdentity{issuer, "member"})
132 if e != nil {
133 t.Fatal(e)
134 }
135 got, e := none.Resolve(ctx, issuer, "member")
136 if e != nil || got.ID != h.ID {
137 t.Fatal(got, e)
138 }
139 for _, id := range []p.HomeIdentity{{"https://foreign.example", "member"}, {issuer, "mail@example.invalid"}, {issuer, ""}} {
140 if _, e = s.EnsureHome(ctx, id); e != p.ErrRefused {
141 t.Fatal(e)
142 }
143 }
144}
145func TestConcurrentCreationAndImmutableIDs(t *testing.T) {
146 d, s := fixture(t, true)
147 ctx := context.Background()
148 var wg sync.WaitGroup
149 ids := make(chan int64, 16)
150 errs := make(chan error, 16)
151 for range 16 {
152 wg.Add(1)
153 go func() {
154 defer wg.Done()
155 v, e := s.EnsureHome(ctx, p.HomeIdentity{issuer, "same"})
156 if e != nil {
157 errs <- e
158 } else {
159 ids <- v.ID
160 }
161 }()
162 }
163 wg.Wait()
164 close(ids)
165 close(errs)
166 for e := range errs {
167 t.Fatal(e)
168 }
169 for id := range ids {
170 if id != p.HomeIDMin {
171 t.Fatal(id)
172 }
173 }
174 a := local(t, d, "local@example.invalid")
175 for _, q := range []string{
176 `DELETE FROM people`, `UPDATE people SET id=id+1`, `DELETE FROM docs_principals`, `UPDATE docs_principals SET kind='local' WHERE kind='home'`,
177 `DELETE FROM docs_home_identities`, `UPDATE docs_home_identities SET subject='other'`, `UPDATE docs_home_allocator SET high_water=high_water-1`,
178 fmt.Sprintf(`INSERT INTO people(id,email,created_at) VALUES(%d,'collision@example.invalid',CURRENT_TIMESTAMP)`, p.HomeIDMin),
179 fmt.Sprintf(`INSERT INTO docs_home_identities VALUES('%s','wrong-kind',%d)`, issuer, a),
180 `INSERT INTO docs_identity_links VALUES('https://id.oficina.works','missing',12345)`,
181 } {
182 if _, e := d.Writer().Exec(q); e == nil {
183 t.Fatalf("accepted mutation: %s", q)
184 }
185 }
186 if _, e := migrate.Apply(ctx, d, p.Schema); e != nil {
187 t.Fatal(e)
188 }
189 if _, e := migrate.Apply(ctx, d, migrate.Merge(docs.Schema, linkstore.Schema)); e != nil {
190 t.Fatal("older known migration set rejected newer ledger", e)
191 }
192 rows, e := d.Writer().Query(`PRAGMA foreign_key_check`)
193 if e != nil {
194 t.Fatal(e)
195 }
196 defer rows.Close()
197 if rows.Next() {
198 t.Fatal("foreign key damage")
199 }
200}
201func TestSentinelsReferencesAndExhaustion(t *testing.T) {
202 d, s := fixture(t, true)
203 ctx := context.Background()
204 h, e := s.EnsureHome(ctx, p.HomeIdentity{issuer, "owner"})
205 if e != nil {
206 t.Fatal(e)
207 }
208 for i, owner := range []int64{0, h.ID} {
209 exec(t, d, `INSERT INTO documents(id,sqid,title,guid,client_id,created_at,updated_at,owner_id)VALUES(?,?,?,?,1,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,?)`, i+1, fmt.Sprint(i), "Title", "guid", owner)
210 }
211 exec(t, d, `INSERT INTO grants(doc_id,subject,role,created_by)VALUES(1,'reader@example.invalid','reader',0)`)
212 exec(t, d, `UPDATE documents SET owner_id=owner_id,title=title`)
213 exec(t, d, `UPDATE grants SET created_by=created_by`)
214 for _, q := range []string{`UPDATE documents SET owner_id=1`, `UPDATE grants SET created_by=1`, `UPDATE documents SET owner_id=-1`} {
215 if _, e = d.Writer().Exec(q); e == nil {
216 t.Fatal(q)
217 }
218 }
219 exec(t, d, `UPDATE docs_home_allocator SET high_water=?`, p.HomeIDMax)
220 if _, e = s.EnsureHome(ctx, p.HomeIdentity{issuer, "past-cap"}); e != p.ErrRefused {
221 t.Fatal(e)
222 }
223 if _, e = s.Resolve(ctx, issuer, "past-cap"); e != p.ErrRefused {
224 t.Fatal(e)
225 }
226}
227func TestPreflightIsAtomicAndPreservesExistingSchema(t *testing.T) {
228 for _, q := range []string{
229 fmt.Sprintf(`INSERT INTO people VALUES(%d,'range@example.invalid',CURRENT_TIMESTAMP)`, p.HomeIDMin),
230 `INSERT INTO documents(sqid,title,guid,client_id,created_at,updated_at,owner_id)VALUES('x','Title','g',1,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,12)`,
231 `INSERT INTO grants(doc_id,subject,role,created_by)VALUES(1,'reader@example.invalid','reader',12)`,
232 } {
233 t.Run(q, func(t *testing.T) {
234 d, _ := fixture(t, false)
235 exec(t, d, q)
236 if _, e := migrate.Apply(context.Background(), d, p.Schema); e == nil {
237 t.Fatal("bad preflight accepted")
238 }
239 var n int
240 if e := d.Writer().QueryRow(`SELECT COUNT(*) FROM sqlite_master WHERE name LIKE 'docs_principal%' OR name LIKE 'docs_home_%'`).Scan(&n); e != nil || n != 0 {
241 t.Fatal("partial migration", n, e)
242 }
243 })
244 }
245}
246
247func TestRequiresForeignKeysAndPreservesEncodedBytes(t *testing.T) {
248 d, _ := fixture(t, false)
249 id := local(t, d, "owner@example.invalid")
250 exec(t, d, `INSERT INTO documents(sqid,title,guid,client_id,created_at,updated_at,owner_id) VALUES('doc','kept','guid',1,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,?)`, id)
251 exec(t, d, `INSERT INTO doc_updates(doc_id,seq,update_bin,created_at,content_encoding)VALUES(1,1,X'00FFAABB',CURRENT_TIMESTAMP,1)`)
252 exec(t, d, `INSERT INTO doc_snapshots(doc_id,seq,state,abi_version,created_at,content_encoding)VALUES(1,1,X'010203FF',2,CURRENT_TIMESTAMP,1)`)
253 exec(t, d, `INSERT INTO content_sealing VALUES(1,1,'fixed-key-fingerprint',X'090807',0)`)
254 if _, e := migrate.Apply(context.Background(), d, p.Schema); e != nil {
255 t.Fatal(e)
256 }
257 var update, snapshot, key string
258 if e := d.Writer().QueryRow(`SELECT hex(update_bin)||':'||content_encoding FROM doc_updates`).Scan(&update); e != nil {
259 t.Fatal(e)
260 }
261 if e := d.Writer().QueryRow(`SELECT hex(state)||':'||content_encoding FROM doc_snapshots`).Scan(&snapshot); e != nil {
262 t.Fatal(e)
263 }
264 if e := d.Writer().QueryRow(`SELECT key_fingerprint||':'||hex(key_check)||':'||encoding FROM content_sealing`).Scan(&key); e != nil {
265 t.Fatal(e)
266 }
267 if update != "00FFAABB:1" || snapshot != "010203FF:1" || key != "fixed-key-fingerprint:090807:1" {
268 t.Fatal(update, snapshot, key)
269 }
270 exec(t, d, `PRAGMA foreign_keys=OFF`)
271 if _, e := p.New(d.Writer(), p.Config{Issuer: issuer, Team: "A1"}); e != p.ErrRefused {
272 t.Fatal("foreign keys disabled", e)
273 }
274}
275