precedence_test.go raw
1 package policy
2
3 import (
4 "testing"
5
6 "next.orly.dev/pkg/lol/chk"
7 "next.orly.dev/pkg/nostr/encoders/hex"
8 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
9 )
10
11 // TestPolicyPrecedenceRules verifies the correct evaluation order and precedence
12 // of different policy fields, clarifying the exact behavior after fixes.
13 //
14 // Evaluation Order (as fixed):
15 // 1. Universal constraints (size, tags, timestamps)
16 // 2. Explicit denials (highest priority)
17 // 3. Privileged access (ONLY if no allow lists)
18 // 4. Exclusive allow lists (authoritative when present)
19 // 5. Privileged final check
20 // 6. Default policy
21 func TestPolicyPrecedenceRules(t *testing.T) {
22 // Generate test keypairs
23 aliceSigner := p8k.MustNew()
24 if err := aliceSigner.Generate(); chk.E(err) {
25 t.Fatalf("Failed to generate alice signer: %v", err)
26 }
27 alicePubkey := aliceSigner.Pub()
28
29 bobSigner := p8k.MustNew()
30 if err := bobSigner.Generate(); chk.E(err) {
31 t.Fatalf("Failed to generate bob signer: %v", err)
32 }
33 bobPubkey := bobSigner.Pub()
34
35 charlieSigner := p8k.MustNew()
36 if err := charlieSigner.Generate(); chk.E(err) {
37 t.Fatalf("Failed to generate charlie signer: %v", err)
38 }
39 charliePubkey := charlieSigner.Pub()
40
41 // ===================================================================
42 // Test 1: Deny List Has Highest Priority
43 // ===================================================================
44 t.Run("Deny List Overrides Everything", func(t *testing.T) {
45 policy := &P{
46 DefaultPolicy: "allow",
47 rules: map[int]Rule{
48 100: {
49 Description: "Deny overrides allow and privileged",
50 AccessControl: AccessControl{
51 WriteAllow: []string{hex.Enc(alicePubkey)}, // Alice in allow list
52 WriteDeny: []string{hex.Enc(alicePubkey)}, // But also in deny list
53 },
54 Constraints: Constraints{
55 Privileged: true, // And it's privileged
56 },
57 },
58 },
59 }
60
61 // Alice creates an event (she's author, in allow list, but also in deny list)
62 event := createTestEvent(t, aliceSigner, "test", 100)
63
64 // Should be DENIED because deny list has highest priority
65 allowed, err := policy.CheckPolicy("write", event, alicePubkey, "127.0.0.1")
66 if err != nil {
67 t.Fatalf("Unexpected error: %v", err)
68 }
69 if allowed {
70 t.Error("FAIL: User in deny list should be denied even if in allow list and privileged")
71 } else {
72 t.Log("PASS: Deny list correctly overrides allow list and privileged")
73 }
74 })
75
76 // ===================================================================
77 // Test 2: Allow List OR Privileged (Either grants access)
78 // ===================================================================
79 t.Run("Allow List OR Privileged Access", func(t *testing.T) {
80 policy := &P{
81 DefaultPolicy: "allow",
82 rules: map[int]Rule{
83 200: {
84 Description: "Privileged with allow list",
85 AccessControl: AccessControl{
86 ReadAllow: []string{hex.Enc(bobPubkey)}, // Only Bob in allow list
87 },
88 Constraints: Constraints{
89 Privileged: true,
90 },
91 },
92 },
93 }
94
95 // Alice creates event
96 event := createTestEvent(t, aliceSigner, "secret", 200)
97
98 // Test 2a: Alice is author (privileged) but NOT in allow list - should be ALLOWED (OR logic)
99 allowed, err := policy.CheckPolicy("read", event, alicePubkey, "127.0.0.1")
100 if err != nil {
101 t.Fatalf("Unexpected error: %v", err)
102 }
103 if !allowed {
104 t.Error("FAIL: Author should be allowed via privileged (OR logic)")
105 } else {
106 t.Log("PASS: Author allowed via privileged despite not in allow list (OR logic)")
107 }
108
109 // Test 2b: Bob is in allow list - should be ALLOWED
110 allowed, err = policy.CheckPolicy("read", event, bobPubkey, "127.0.0.1")
111 if err != nil {
112 t.Fatalf("Unexpected error: %v", err)
113 }
114 if !allowed {
115 t.Error("FAIL: User in allow list should be allowed")
116 } else {
117 t.Log("PASS: User in allow list correctly allowed")
118 }
119
120 // Test 2c: Charlie in p-tag but not in allow list - should be ALLOWED (OR logic)
121 addPTag(event, charliePubkey)
122 allowed, err = policy.CheckPolicy("read", event, charliePubkey, "127.0.0.1")
123 if err != nil {
124 t.Fatalf("Unexpected error: %v", err)
125 }
126 if !allowed {
127 t.Error("FAIL: User in p-tag should be allowed via privileged (OR logic)")
128 } else {
129 t.Log("PASS: User in p-tag allowed via privileged despite not in allow list (OR logic)")
130 }
131 })
132
133 // ===================================================================
134 // Test 3: Privileged Without Allow List Grants Access
135 // ===================================================================
136 t.Run("Privileged Grants Access When No Allow List", func(t *testing.T) {
137 policy := &P{
138 DefaultPolicy: "deny", // Default deny to make test clearer
139 rules: map[int]Rule{
140 300: {
141 Description: "Privileged without allow list",
142 Constraints: Constraints{
143 Privileged: true,
144 },
145 // NO ReadAllow or WriteAllow specified
146 },
147 },
148 }
149
150 // Alice creates event with Bob in p-tag
151 event := createTestEvent(t, aliceSigner, "message", 300)
152 addPTag(event, bobPubkey)
153
154 // Test 3a: Alice (author) should be ALLOWED (privileged, no allow list)
155 allowed, err := policy.CheckPolicy("read", event, alicePubkey, "127.0.0.1")
156 if err != nil {
157 t.Fatalf("Unexpected error: %v", err)
158 }
159
160
161 if !allowed {
162 t.Error("FAIL: Author should be allowed when privileged and no allow list")
163 } else {
164 t.Log("PASS: Privileged correctly grants access to author when no allow list")
165 }
166
167 // Test 3b: Bob (in p-tag) should be ALLOWED (privileged, no allow list)
168 allowed, err = policy.CheckPolicy("read", event, bobPubkey, "127.0.0.1")
169 if err != nil {
170 t.Fatalf("Unexpected error: %v", err)
171 }
172 if !allowed {
173 t.Error("FAIL: P-tagged user should be allowed when privileged and no allow list")
174 } else {
175 t.Log("PASS: Privileged correctly grants access to p-tagged user when no allow list")
176 }
177
178 // Test 3c: Charlie (not involved) should be DENIED
179 allowed, err = policy.CheckPolicy("read", event, charliePubkey, "127.0.0.1")
180 if err != nil {
181 t.Fatalf("Unexpected error: %v", err)
182 }
183 if allowed {
184 t.Error("FAIL: Non-involved user should be denied for privileged event")
185 } else {
186 t.Log("PASS: Privileged correctly denies non-involved user")
187 }
188 })
189
190 // ===================================================================
191 // Test 4: Allow List Without Privileged Is Exclusive
192 // ===================================================================
193 t.Run("Allow List Exclusive Without Privileged", func(t *testing.T) {
194 policy := &P{
195 DefaultPolicy: "allow", // Even with allow default
196 rules: map[int]Rule{
197 400: {
198 Description: "Allow list only",
199 AccessControl: AccessControl{
200 WriteAllow: []string{hex.Enc(alicePubkey)}, // Only Alice
201 },
202 // NO Privileged flag
203 },
204 },
205 }
206
207 // Test 4a: Alice should be ALLOWED (in allow list)
208 aliceEvent := createTestEvent(t, aliceSigner, "alice msg", 400)
209 allowed, err := policy.CheckPolicy("write", aliceEvent, alicePubkey, "127.0.0.1")
210 if err != nil {
211 t.Fatalf("Unexpected error: %v", err)
212 }
213 if !allowed {
214 t.Error("FAIL: User in allow list should be allowed")
215 } else {
216 t.Log("PASS: Allow list correctly allows listed user")
217 }
218
219 // Test 4b: Bob should be DENIED (not in allow list, even with allow default)
220 bobEvent := createTestEvent(t, bobSigner, "bob msg", 400)
221 allowed, err = policy.CheckPolicy("write", bobEvent, bobPubkey, "127.0.0.1")
222 if err != nil {
223 t.Fatalf("Unexpected error: %v", err)
224 }
225 if allowed {
226 t.Error("FAIL: User not in allow list should be denied despite allow default")
227 } else {
228 t.Log("PASS: Allow list correctly excludes non-listed user")
229 }
230 })
231
232 // ===================================================================
233 // Test 5: Complex Precedence Chain
234 // ===================================================================
235 t.Run("Complex Precedence Chain", func(t *testing.T) {
236 policy := &P{
237 DefaultPolicy: "allow",
238 rules: map[int]Rule{
239 500: {
240 Description: "Complex rules",
241 AccessControl: AccessControl{
242 WriteAllow: []string{hex.Enc(alicePubkey), hex.Enc(bobPubkey)},
243 WriteDeny: []string{hex.Enc(bobPubkey)}, // Bob denied despite being in allow
244 },
245 Constraints: Constraints{
246 Privileged: true,
247 },
248 },
249 },
250 }
251
252 // Test 5a: Alice in allow, not in deny - ALLOWED
253 aliceEvent := createTestEvent(t, aliceSigner, "alice", 500)
254 allowed, err := policy.CheckPolicy("write", aliceEvent, alicePubkey, "127.0.0.1")
255 if err != nil {
256 t.Fatalf("Unexpected error: %v", err)
257 }
258 if !allowed {
259 t.Error("FAIL: Alice should be allowed (in allow, not in deny)")
260 } else {
261 t.Log("PASS: User in allow and not in deny is allowed")
262 }
263
264 // Test 5b: Bob in allow AND deny - DENIED (deny wins)
265 bobEvent := createTestEvent(t, bobSigner, "bob", 500)
266 allowed, err = policy.CheckPolicy("write", bobEvent, bobPubkey, "127.0.0.1")
267 if err != nil {
268 t.Fatalf("Unexpected error: %v", err)
269 }
270 if allowed {
271 t.Error("FAIL: Bob should be denied (deny list overrides allow list)")
272 } else {
273 t.Log("PASS: Deny list correctly overrides allow list")
274 }
275
276 // Test 5c: Charlie not in allow - DENIED (even though he's author of his event)
277 charlieEvent := createTestEvent(t, charlieSigner, "charlie", 500)
278 allowed, err = policy.CheckPolicy("write", charlieEvent, charliePubkey, "127.0.0.1")
279 if err != nil {
280 t.Fatalf("Unexpected error: %v", err)
281 }
282 if allowed {
283 t.Error("FAIL: Charlie should be denied (not in allow list)")
284 } else {
285 t.Log("PASS: Allow list correctly excludes non-listed privileged author")
286 }
287 })
288
289 // ===================================================================
290 // Test 6: Default Policy Application
291 // ===================================================================
292 t.Run("Default Policy Only When No Rules", func(t *testing.T) {
293 // Test 6a: With allow default and no rules
294 policyAllow := &P{
295 DefaultPolicy: "allow",
296 rules: map[int]Rule{
297 // No rule for kind 600
298 },
299 }
300
301 event := createTestEvent(t, aliceSigner, "test", 600)
302 allowed, err := policyAllow.CheckPolicy("write", event, alicePubkey, "127.0.0.1")
303 if err != nil {
304 t.Fatalf("Unexpected error: %v", err)
305 }
306 if !allowed {
307 t.Error("FAIL: Default allow should permit when no rules")
308 } else {
309 t.Log("PASS: Default allow correctly applied when no rules")
310 }
311
312 // Test 6b: With deny default and no rules
313 policyDeny := &P{
314 DefaultPolicy: "deny",
315 rules: map[int]Rule{
316 // No rule for kind 600
317 },
318 }
319
320 allowed, err = policyDeny.CheckPolicy("write", event, alicePubkey, "127.0.0.1")
321 if err != nil {
322 t.Fatalf("Unexpected error: %v", err)
323 }
324 if allowed {
325 t.Error("FAIL: Default deny should block when no rules")
326 } else {
327 t.Log("PASS: Default deny correctly applied when no rules")
328 }
329
330 // Test 6c: Default does NOT apply when allow list exists
331 policyWithRule := &P{
332 DefaultPolicy: "allow", // Allow default
333 rules: map[int]Rule{
334 700: {
335 AccessControl: AccessControl{
336 WriteAllow: []string{hex.Enc(bobPubkey)}, // Only Bob
337 },
338 },
339 },
340 }
341
342 eventKind700 := createTestEvent(t, aliceSigner, "alice", 700)
343 allowed, err = policyWithRule.CheckPolicy("write", eventKind700, alicePubkey, "127.0.0.1")
344 if err != nil {
345 t.Fatalf("Unexpected error: %v", err)
346 }
347 if allowed {
348 t.Error("FAIL: Default allow should NOT override exclusive allow list")
349 } else {
350 t.Log("PASS: Allow list correctly overrides default policy")
351 }
352 })
353 }
354