oficina / home Public

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

Plain git — no account needed to clone.

Download

Download this file

1package operator
2
3import (
4 "bytes"
5 "context"
6 "encoding/base64"
7 "strings"
8 "testing"
9 "time"
10
11 "amadan.net/oficina/home/internal/home/directorydata"
12 "github.com/go-json-experiment/json"
13)
14
15func pendingFixture(t *testing.T) (*directoryFixture, VerificationInput) {
16 t.Helper()
17 f := newDirectoryFixture(t)
18 f.initialize(t)
19 if _, err := f.seed(t, 1, "alpha"); err != nil {
20 t.Fatal(err)
21 }
22 raw := f.input(t, "directory.provision-consumer", func(b InputBase) any {
23 return ProvisionInput{b, token(10), "Uk", "docs", "alpha-docs", "https://alpha.docs.oficina.works", 0}
24 })
25 out, err := f.directory.Execute(context.Background(), raw)
26 if err != nil {
27 t.Fatal(err)
28 }
29 var result Result
30 if err = json.Unmarshal(out, &result); err != nil {
31 t.Fatal(err)
32 }
33 f.bearer = result.Bearer
34 var digest []byte
35 if err := f.db.QueryRow("SELECT record_digest FROM directory_operator_consumer_records").Scan(&digest); err != nil {
36 t.Fatal(err)
37 }
38 return f, VerificationInput{Consumer: "alpha-docs", TeamSQID: "Uk", App: "docs", Audience: "https://alpha.docs.oficina.works", Generation: token(10), RecordSequence: 1, RecordDigest: base64.RawURLEncoding.EncodeToString(digest), Epoch: 2, Owner: "pr_aaaaaaaaaaaaaaaaaaaaaaaaaa", Issued: f.now.Unix(), Expires: f.now.Unix() + 600}
39}
40func openWindow(t *testing.T, f *directoryFixture, in VerificationInput) ([]byte, []byte, Result) {
41 t.Helper()
42 raw := f.input(t, "directory.verify-consumer", func(b InputBase) any { in.InputBase = b; return in })
43 out, err := f.directory.Execute(context.Background(), raw)
44 if err != nil {
45 t.Fatal(err)
46 }
47 var result Result
48 if err = json.Unmarshal(out, &result); err != nil {
49 t.Fatal(err)
50 }
51 return raw, out, result
52}
53func activation(in VerificationInput, attempt string) ActivationEvidence {
54 return ActivationEvidence{Version: 1, Policy: "oficina/team-activation/v1", Attempt: attempt, Consumer: in.Consumer, TeamSQID: in.TeamSQID, App: in.App, Audience: in.Audience, Generation: in.Generation, RecordSequence: in.RecordSequence, RecordDigest: in.RecordDigest, Epoch: in.Epoch, Owner: in.Owner, AppVersion: strings.Repeat("a", 40), ArtifactDigest: token(80), EnvironmentBinding: token(81), ManifestDigest: token(82), AliasHost: "alpha.docs.oficina.works", Started: in.Issued, Completed: in.Issued}
55}
56func TestVerificationAbsoluteExpiryAndExactReplay(t *testing.T) {
57 f, in := pendingFixture(t)
58 raw, out, result := openWindow(t, f, in)
59 *f.now = f.now.Add(600 * time.Second)
60 replay, err := f.directory.Execute(context.Background(), raw)
61 if err != nil || !bytes.Equal(replay, out) || result.Expires != in.Expires {
62 t.Fatal("expired replay changed window", err)
63 }
64 in.Issued = f.now.Unix()
65 in.Expires = in.Issued + 600
66 _, _, next := openWindow(t, f, in)
67 if next.Attempt == result.Attempt || countRows(t, f.db, "verification_attempts") != 2 || countRows(t, f.db, "verification_active") != 1 || countRows(t, f.db, "verification_outcomes") != 3 {
68 t.Fatal("replacement lost immutable history")
69 }
70 raw = f.input(t, "directory.verify-consumer", func(b InputBase) any { in.InputBase = b; return in })
71 if _, err = f.directory.Execute(context.Background(), raw); err != ErrRefused {
72 t.Fatal("overlap renewed window", err)
73 }
74}
75func TestVerificationRejectsWrongScopeBeforeStanding(t *testing.T) {
76 mutations := map[string]func(*VerificationInput){
77 "long": func(v *VerificationInput) { v.Expires++ }, "expired": func(v *VerificationInput) { v.Expires = v.Issued }, "future": func(v *VerificationInput) { v.Issued++; v.Expires++ },
78 "epoch": func(v *VerificationInput) { v.Epoch++ }, "owner": func(v *VerificationInput) { v.Owner = "pr_bbbbbbbbbbbbbbbbbbbbbbbbbb" }, "record": func(v *VerificationInput) { v.RecordDigest = token(99) }, "generation": func(v *VerificationInput) { v.Generation = token(99) }, "audience": func(v *VerificationInput) { v.Audience = "https://beta.docs.oficina.works" },
79 }
80 for name, mutate := range mutations {
81 t.Run(name, func(t *testing.T) {
82 f, in := pendingFixture(t)
83 mutate(&in)
84 raw := f.input(t, "directory.verify-consumer", func(b InputBase) any { in.InputBase = b; return in })
85 if _, err := f.directory.Execute(context.Background(), raw); err != ErrRefused {
86 t.Fatal(err)
87 }
88 if countRows(t, f.db, "verification_attempts") != 0 || countRows(t, f.db, "verification_active") != 0 {
89 t.Fatal("rejected attempt granted standing")
90 }
91 })
92 }
93}
94func TestActivationExactEvidenceAndAtomicReplay(t *testing.T) {
95 f, in := pendingFixture(t)
96 _, _, window := openWindow(t, f, in)
97 evidence := activation(in, window.Attempt)
98 raw := f.input(t, "directory.activate-consumer", func(b InputBase) any { return ActivationInput{b, evidence} })
99 out, err := f.directory.Execute(context.Background(), raw)
100 if err != nil {
101 t.Fatal(err)
102 }
103 replay, err := f.directory.Execute(context.Background(), raw)
104 if err != nil || !bytes.Equal(out, replay) {
105 t.Fatal("activation replay", err)
106 }
107 var state string
108 if err = f.db.QueryRow("SELECT state FROM team_instances").Scan(&state); err != nil || state != "live" || countRows(t, f.db, "verification_active") != 0 || countRows(t, f.db, "verification_outcomes") != 2 {
109 t.Fatal("incomplete activation", err)
110 }
111}
112func TestActivationRejectsChangedStateAndEvidence(t *testing.T) {
113 cases := map[string]func(*testing.T, *directoryFixture, *ActivationEvidence){
114 "token": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) {
115 if _, e := f.db.Exec("DELETE FROM roster_tokens"); e != nil {
116 t.Fatal(e)
117 }
118 },
119 "alias": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) {
120 p.AliasHost = "beta.docs.oficina.works"
121 },
122 "attempt": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) { p.Attempt = token(95) },
123 "version": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) { p.AppVersion = "latest" },
124 "future": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) { p.Completed++ },
125 "expired": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) {
126 *f.now = f.now.Add(600 * time.Second)
127 p.Completed = f.now.Unix()
128 },
129 "epoch": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) {
130 if _, e := f.db.Exec("INSERT INTO memberships(team_id,person_ref,role) VALUES(1,'pr_bbbbbbbbbbbbbbbbbbbbbbbbbb','member')"); e != nil {
131 t.Fatal(e)
132 }
133 },
134 "owner": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) {
135 if _, e := f.db.Exec("DELETE FROM memberships"); e != nil {
136 t.Fatal(e)
137 }
138 },
139 "pointer": func(t *testing.T, f *directoryFixture, p *ActivationEvidence) {
140 if _, e := f.db.Exec("UPDATE directory_operator_consumer_records SET sequence=2"); e != nil {
141 t.Fatal(e)
142 }
143 },
144 }
145 for name, mutate := range cases {
146 t.Run(name, func(t *testing.T) {
147 f, in := pendingFixture(t)
148 _, _, window := openWindow(t, f, in)
149 p := activation(in, window.Attempt)
150 mutate(t, f, &p)
151 raw := f.input(t, "directory.activate-consumer", func(b InputBase) any { return ActivationInput{b, p} })
152 if _, err := f.directory.Execute(context.Background(), raw); err != ErrRefused {
153 t.Fatal(err)
154 }
155 var state string
156 if err := f.db.QueryRow("SELECT state FROM team_instances").Scan(&state); err != nil || state != "pending" || countRows(t, f.db, "verification_active") != 1 || countRows(t, f.db, "verification_outcomes") != 1 {
157 t.Fatal("failed activation mutated", err)
158 }
159 })
160 }
161}
162
163func TestOperatorWindowControlsActualRosterReader(t *testing.T) {
164 f, in := pendingFixture(t)
165 source, err := directorydata.NewRosterSource(f.db, f.engine.sealer, f.directory.policy, f.directory.owner.directory)
166 if err != nil {
167 t.Fatal(err)
168 }
169 req := directorydata.RosterRequest{Token: f.bearer, Team: "Uk", Fetch: token(92)}
170 check := func(want int) {
171 t.Helper()
172 _, status, err := source.Read(context.Background(), req, *f.now)
173 if status != want || (want == 200 && err != nil) {
174 t.Fatalf("reader status %d want %d: %v", status, want, err)
175 }
176 }
177 check(503)
178 raw, _, window := openWindow(t, f, in)
179 check(200)
180 *f.now = f.now.Add(599 * time.Second)
181 check(200)
182 *f.now = f.now.Add(time.Second)
183 check(503)
184 if _, err = f.directory.Execute(context.Background(), raw); err != nil {
185 t.Fatal(err)
186 }
187 check(503)
188 in.Issued = f.now.Unix()
189 in.Expires = in.Issued + 600
190 _, _, window = openWindow(t, f, in)
191 check(200)
192 proof := activation(in, window.Attempt)
193 raw = f.input(t, "directory.activate-consumer", func(b InputBase) any { return ActivationInput{b, proof} })
194 if _, err = f.directory.Execute(context.Background(), raw); err != nil {
195 t.Fatal(err)
196 }
197 *f.now = f.now.Add(601 * time.Second)
198 check(200)
199}
200