comprehensive_test.go raw
1 package policy
2
3 import (
4 "context"
5 "encoding/json"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "next.orly.dev/pkg/lol/chk"
11 "next.orly.dev/pkg/nostr/encoders/hex"
12 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
13 )
14
15 // TestPolicyDefinitionOfDone tests all requirements from the GitHub issue
16 // Issue: https://git.nostrdev.com/mleku/next.orly.dev/issues/5
17 //
18 // Requirements:
19 // 1. Configure relay to accept only certain kind events
20 // 2. Scenario A: Only certain users should be allowed to write events
21 // 3. Scenario B: Only certain users should be allowed to read events
22 // 4. Scenario C: Only users involved in events should be able to read the events (privileged)
23 // 5. Scenario D: Scripting option for complex validation
24 func TestPolicyDefinitionOfDone(t *testing.T) {
25 // Generate test keypairs
26 allowedSigner := p8k.MustNew()
27 if err := allowedSigner.Generate(); chk.E(err) {
28 t.Fatalf("Failed to generate allowed signer: %v", err)
29 }
30 allowedPubkey := allowedSigner.Pub()
31 allowedPubkeyHex := hex.Enc(allowedPubkey)
32
33 unauthorizedSigner := p8k.MustNew()
34 if err := unauthorizedSigner.Generate(); chk.E(err) {
35 t.Fatalf("Failed to generate unauthorized signer: %v", err)
36 }
37 unauthorizedPubkey := unauthorizedSigner.Pub()
38 unauthorizedPubkeyHex := hex.Enc(unauthorizedPubkey)
39
40 thirdPartySigner := p8k.MustNew()
41 if err := thirdPartySigner.Generate(); chk.E(err) {
42 t.Fatalf("Failed to generate third party signer: %v", err)
43 }
44 thirdPartyPubkey := thirdPartySigner.Pub()
45
46 t.Logf("Allowed pubkey: %s", allowedPubkeyHex)
47 t.Logf("Unauthorized pubkey: %s", unauthorizedPubkeyHex)
48
49 // ===================================================================
50 // Requirement 1: Configure relay to accept only certain kind events
51 // ===================================================================
52 t.Run("Requirement 1: Kind Whitelist", func(t *testing.T) {
53 // Create policy with kind whitelist
54 policyJSON := map[string]interface{}{
55 "kind": map[string]interface{}{
56 "whitelist": []int{1, 3, 4}, // Only allow kinds 1, 3, 4
57 },
58 }
59
60 policyBytes, err := json.Marshal(policyJSON)
61 if err != nil {
62 t.Fatalf("Failed to marshal policy: %v", err)
63 }
64
65 policy, err := New(policyBytes)
66 if err != nil {
67 t.Fatalf("Failed to create policy: %v", err)
68 }
69
70 // Test: Kind 1 should be allowed (in whitelist)
71 event1 := createTestEvent(t, allowedSigner, "kind 1 event", 1)
72 allowed, err := policy.CheckPolicy("write", event1, allowedPubkey, "127.0.0.1")
73 if err != nil {
74 t.Errorf("Unexpected error: %v", err)
75 }
76 if !allowed {
77 t.Error("FAIL: Kind 1 should be allowed (in whitelist)")
78 } else {
79 t.Log("PASS: Kind 1 is allowed (in whitelist)")
80 }
81
82 // Test: Kind 5 should be denied (not in whitelist)
83 event5 := createTestEvent(t, allowedSigner, "kind 5 event", 5)
84 allowed, err = policy.CheckPolicy("write", event5, allowedPubkey, "127.0.0.1")
85 if err != nil {
86 t.Errorf("Unexpected error: %v", err)
87 }
88 if allowed {
89 t.Error("FAIL: Kind 5 should be denied (not in whitelist)")
90 } else {
91 t.Log("PASS: Kind 5 is denied (not in whitelist)")
92 }
93
94 // Test: Kind 3 should be allowed (in whitelist)
95 event3 := createTestEvent(t, allowedSigner, "kind 3 event", 3)
96 allowed, err = policy.CheckPolicy("write", event3, allowedPubkey, "127.0.0.1")
97 if err != nil {
98 t.Errorf("Unexpected error: %v", err)
99 }
100 if !allowed {
101 t.Error("FAIL: Kind 3 should be allowed (in whitelist)")
102 } else {
103 t.Log("PASS: Kind 3 is allowed (in whitelist)")
104 }
105 })
106
107 // ===================================================================
108 // Requirement 2: Scenario A - Only certain users can write events
109 // ===================================================================
110 t.Run("Scenario A: Per-Kind Write Access Control", func(t *testing.T) {
111 // Create policy with write_allow for kind 10
112 policyJSON := map[string]interface{}{
113 "rules": map[string]interface{}{
114 "10": map[string]interface{}{
115 "description": "Only allowed user can write kind 10",
116 "write_allow": []string{allowedPubkeyHex},
117 },
118 },
119 }
120
121 policyBytes, err := json.Marshal(policyJSON)
122 if err != nil {
123 t.Fatalf("Failed to marshal policy: %v", err)
124 }
125
126 policy, err := New(policyBytes)
127 if err != nil {
128 t.Fatalf("Failed to create policy: %v", err)
129 }
130
131 // Test: Allowed user can write kind 10
132 event10Allowed := createTestEvent(t, allowedSigner, "kind 10 from allowed user", 10)
133 allowed, err := policy.CheckPolicy("write", event10Allowed, allowedPubkey, "127.0.0.1")
134 if err != nil {
135 t.Errorf("Unexpected error: %v", err)
136 }
137 if !allowed {
138 t.Error("FAIL: Allowed user should be able to write kind 10")
139 } else {
140 t.Log("PASS: Allowed user can write kind 10")
141 }
142
143 // Test: Unauthorized user cannot write kind 10
144 event10Unauthorized := createTestEvent(t, unauthorizedSigner, "kind 10 from unauthorized user", 10)
145 allowed, err = policy.CheckPolicy("write", event10Unauthorized, unauthorizedPubkey, "127.0.0.1")
146 if err != nil {
147 t.Errorf("Unexpected error: %v", err)
148 }
149 if allowed {
150 t.Error("FAIL: Unauthorized user should NOT be able to write kind 10")
151 } else {
152 t.Log("PASS: Unauthorized user cannot write kind 10")
153 }
154 })
155
156 // ===================================================================
157 // Requirement 3: Scenario B - Only certain users can read events
158 // ===================================================================
159 t.Run("Scenario B: Per-Kind Read Access Control", func(t *testing.T) {
160 // Create policy with read_allow for kind 20
161 policyJSON := map[string]interface{}{
162 "rules": map[string]interface{}{
163 "20": map[string]interface{}{
164 "description": "Only allowed user can read kind 20",
165 "read_allow": []string{allowedPubkeyHex},
166 },
167 },
168 }
169
170 policyBytes, err := json.Marshal(policyJSON)
171 if err != nil {
172 t.Fatalf("Failed to marshal policy: %v", err)
173 }
174
175 policy, err := New(policyBytes)
176 if err != nil {
177 t.Fatalf("Failed to create policy: %v", err)
178 }
179
180 // Create a kind 20 event (doesn't matter who wrote it)
181 event20 := createTestEvent(t, allowedSigner, "kind 20 event", 20)
182
183 // Test: Allowed user can read kind 20
184 allowed, err := policy.CheckPolicy("read", event20, allowedPubkey, "127.0.0.1")
185 if err != nil {
186 t.Errorf("Unexpected error: %v", err)
187 }
188 if !allowed {
189 t.Error("FAIL: Allowed user should be able to read kind 20")
190 } else {
191 t.Log("PASS: Allowed user can read kind 20")
192 }
193
194 // Test: Unauthorized user cannot read kind 20
195 allowed, err = policy.CheckPolicy("read", event20, unauthorizedPubkey, "127.0.0.1")
196 if err != nil {
197 t.Errorf("Unexpected error: %v", err)
198 }
199 if allowed {
200 t.Error("FAIL: Unauthorized user should NOT be able to read kind 20")
201 } else {
202 t.Log("PASS: Unauthorized user cannot read kind 20")
203 }
204
205 // Test: Unauthenticated user cannot read kind 20
206 allowed, err = policy.CheckPolicy("read", event20, nil, "127.0.0.1")
207 if err != nil {
208 t.Errorf("Unexpected error: %v", err)
209 }
210 if allowed {
211 t.Error("FAIL: Unauthenticated user should NOT be able to read kind 20")
212 } else {
213 t.Log("PASS: Unauthenticated user cannot read kind 20")
214 }
215 })
216
217 // ===================================================================
218 // Requirement 4: Scenario C - Only users involved in events can read (privileged)
219 // ===================================================================
220 t.Run("Scenario C: Privileged Events - Only Parties Involved", func(t *testing.T) {
221 // Create policy with privileged flag for kind 30
222 policyJSON := map[string]interface{}{
223 "rules": map[string]interface{}{
224 "30": map[string]interface{}{
225 "description": "Privileged - only parties involved can read",
226 "privileged": true,
227 },
228 },
229 }
230
231 policyBytes, err := json.Marshal(policyJSON)
232 if err != nil {
233 t.Fatalf("Failed to marshal policy: %v", err)
234 }
235
236 policy, err := New(policyBytes)
237 if err != nil {
238 t.Fatalf("Failed to create policy: %v", err)
239 }
240
241 // Test 1: Author can read their own event
242 event30Author := createTestEvent(t, allowedSigner, "kind 30 authored by allowed user", 30)
243 allowed, err := policy.CheckPolicy("read", event30Author, allowedPubkey, "127.0.0.1")
244 if err != nil {
245 t.Errorf("Unexpected error: %v", err)
246 }
247 if !allowed {
248 t.Error("FAIL: Author should be able to read their own privileged event")
249 } else {
250 t.Log("PASS: Author can read their own privileged event")
251 }
252
253 // Test 2: User in p-tag can read the event
254 event30WithPTag := createTestEvent(t, allowedSigner, "kind 30 with unauthorized in p-tag", 30)
255 addPTag(event30WithPTag, unauthorizedPubkey) // Add unauthorized user to p-tag
256 allowed, err = policy.CheckPolicy("read", event30WithPTag, unauthorizedPubkey, "127.0.0.1")
257 if err != nil {
258 t.Errorf("Unexpected error: %v", err)
259 }
260 if !allowed {
261 t.Error("FAIL: User in p-tag should be able to read privileged event")
262 } else {
263 t.Log("PASS: User in p-tag can read privileged event")
264 }
265
266 // Test 3: Third party (not author, not in p-tag) cannot read
267 event30NoAccess := createTestEvent(t, allowedSigner, "kind 30 for allowed only", 30)
268 allowed, err = policy.CheckPolicy("read", event30NoAccess, thirdPartyPubkey, "127.0.0.1")
269 if err != nil {
270 t.Errorf("Unexpected error: %v", err)
271 }
272 if allowed {
273 t.Error("FAIL: Third party should NOT be able to read privileged event")
274 } else {
275 t.Log("PASS: Third party cannot read privileged event")
276 }
277
278 // Test 4: Unauthenticated user cannot read privileged event
279 allowed, err = policy.CheckPolicy("read", event30NoAccess, nil, "127.0.0.1")
280 if err != nil {
281 t.Errorf("Unexpected error: %v", err)
282 }
283 if allowed {
284 t.Error("FAIL: Unauthenticated user should NOT be able to read privileged event")
285 } else {
286 t.Log("PASS: Unauthenticated user cannot read privileged event")
287 }
288 })
289
290 // ===================================================================
291 // Requirement 5: Scenario D - Scripting support
292 // ===================================================================
293 t.Run("Scenario D: Scripting Support", func(t *testing.T) {
294 // Create temporary directory for test
295 tempDir := t.TempDir()
296 scriptPath := filepath.Join(tempDir, "test-policy.sh")
297
298 // Create a simple test script that accepts events with content "accept"
299 scriptContent := `#!/bin/bash
300 while IFS= read -r line; do
301 if echo "$line" | grep -q '"content":"accept"'; then
302 echo '{"id":"test","action":"accept","msg":"accepted by script"}'
303 else
304 echo '{"id":"test","action":"reject","msg":"rejected by script"}'
305 fi
306 done
307 `
308 if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil {
309 t.Fatalf("Failed to write test script: %v", err)
310 }
311
312 // Create policy with script
313 policyJSON := map[string]interface{}{
314 "rules": map[string]interface{}{
315 "40": map[string]interface{}{
316 "description": "Script-based validation",
317 "script": scriptPath,
318 },
319 },
320 }
321
322 policyBytes, err := json.Marshal(policyJSON)
323 if err != nil {
324 t.Fatalf("Failed to marshal policy: %v", err)
325 }
326
327 policy, err := New(policyBytes)
328 if err != nil {
329 t.Fatalf("Failed to create policy: %v", err)
330 }
331
332 // Initialize policy manager
333 ctx, cancel := context.WithCancel(context.Background())
334 defer cancel()
335 policy.manager = &PolicyManager{
336 ctx: ctx,
337 cancel: cancel,
338 configDir: tempDir,
339 scriptPath: scriptPath,
340 enabled: true,
341 runners: make(map[string]*ScriptRunner),
342 }
343
344 // Test: Event with "accept" content should be accepted
345 eventAccept := createTestEvent(t, allowedSigner, "accept", 40)
346 allowed, err := policy.CheckPolicy("write", eventAccept, allowedPubkey, "127.0.0.1")
347 if err != nil {
348 t.Logf("Script check failed (expected if script not running): %v", err)
349 t.Log("SKIP: Script execution requires policy manager to be fully running")
350 } else if !allowed {
351 t.Log("INFO: Script rejected event (may be expected if script not running)")
352 } else {
353 t.Log("PASS: Script accepted event with 'accept' content")
354 }
355
356 // Note: Full script testing requires the policy manager to be running,
357 // which is tested in policy_integration_test.go
358 t.Log("INFO: Full script validation tested in integration tests")
359 })
360
361 // ===================================================================
362 // Combined Scenarios
363 // ===================================================================
364 t.Run("Combined: Kind Whitelist + Write Access + Privileged", func(t *testing.T) {
365 // Create comprehensive policy
366 policyJSON := map[string]interface{}{
367 "kind": map[string]interface{}{
368 "whitelist": []int{50, 51}, // Only kinds 50 and 51
369 },
370 "rules": map[string]interface{}{
371 "50": map[string]interface{}{
372 "description": "Write-restricted kind",
373 "write_allow": []string{allowedPubkeyHex},
374 },
375 "51": map[string]interface{}{
376 "description": "Privileged kind",
377 "privileged": true,
378 },
379 },
380 }
381
382 policyBytes, err := json.Marshal(policyJSON)
383 if err != nil {
384 t.Fatalf("Failed to marshal policy: %v", err)
385 }
386
387 policy, err := New(policyBytes)
388 if err != nil {
389 t.Fatalf("Failed to create policy: %v", err)
390 }
391
392 // Test 1: Kind 50 with allowed user should pass
393 event50Allowed := createTestEvent(t, allowedSigner, "kind 50 allowed", 50)
394 allowed, err := policy.CheckPolicy("write", event50Allowed, allowedPubkey, "127.0.0.1")
395 if err != nil {
396 t.Errorf("Unexpected error: %v", err)
397 }
398 if !allowed {
399 t.Error("FAIL: Kind 50 with allowed user should pass")
400 } else {
401 t.Log("PASS: Kind 50 with allowed user passes")
402 }
403
404 // Test 2: Kind 50 with unauthorized user should fail
405 event50Unauthorized := createTestEvent(t, unauthorizedSigner, "kind 50 unauthorized", 50)
406 allowed, err = policy.CheckPolicy("write", event50Unauthorized, unauthorizedPubkey, "127.0.0.1")
407 if err != nil {
408 t.Errorf("Unexpected error: %v", err)
409 }
410 if allowed {
411 t.Error("FAIL: Kind 50 with unauthorized user should fail")
412 } else {
413 t.Log("PASS: Kind 50 with unauthorized user fails")
414 }
415
416 // Test 3: Kind 100 (not in whitelist) should fail regardless of user
417 event100 := createTestEvent(t, allowedSigner, "kind 100 not in whitelist", 100)
418 allowed, err = policy.CheckPolicy("write", event100, allowedPubkey, "127.0.0.1")
419 if err != nil {
420 t.Errorf("Unexpected error: %v", err)
421 }
422 if allowed {
423 t.Error("FAIL: Kind 100 (not in whitelist) should fail")
424 } else {
425 t.Log("PASS: Kind 100 (not in whitelist) fails")
426 }
427
428 // Test 4: Kind 51 (privileged) - author can write
429 event51Author := createTestEvent(t, allowedSigner, "kind 51 by author", 51)
430 allowed, err = policy.CheckPolicy("write", event51Author, allowedPubkey, "127.0.0.1")
431 if err != nil {
432 t.Errorf("Unexpected error: %v", err)
433 }
434 if !allowed {
435 t.Error("FAIL: Author should be able to write their own privileged event")
436 } else {
437 t.Log("PASS: Author can write their own privileged event")
438 }
439
440 // Test 5: Kind 51 (privileged) - third party cannot read
441 allowed, err = policy.CheckPolicy("read", event51Author, thirdPartyPubkey, "127.0.0.1")
442 if err != nil {
443 t.Errorf("Unexpected error: %v", err)
444 }
445 if allowed {
446 t.Error("FAIL: Third party should NOT be able to read privileged event")
447 } else {
448 t.Log("PASS: Third party cannot read privileged event")
449 }
450 })
451 }
452
453 // TestDefaultPolicy tests the default_policy configuration
454 func TestDefaultPolicy(t *testing.T) {
455 allowedSigner := p8k.MustNew()
456 if err := allowedSigner.Generate(); chk.E(err) {
457 t.Fatalf("Failed to generate signer: %v", err)
458 }
459
460 t.Run("default policy allow", func(t *testing.T) {
461 policyJSON := map[string]interface{}{
462 "default_policy": "allow",
463 }
464
465 policyBytes, err := json.Marshal(policyJSON)
466 if err != nil {
467 t.Fatalf("Failed to marshal policy: %v", err)
468 }
469
470 policy, err := New(policyBytes)
471 if err != nil {
472 t.Fatalf("Failed to create policy: %v", err)
473 }
474
475 // Event without specific rule should be allowed
476 event := createTestEvent(t, allowedSigner, "test event", 999)
477 allowed, err := policy.CheckPolicy("write", event, allowedSigner.Pub(), "127.0.0.1")
478 if err != nil {
479 t.Errorf("Unexpected error: %v", err)
480 }
481 if !allowed {
482 t.Error("FAIL: Event should be allowed with default_policy=allow")
483 } else {
484 t.Log("PASS: Event allowed with default_policy=allow")
485 }
486 })
487
488 t.Run("default policy deny", func(t *testing.T) {
489 policyJSON := map[string]interface{}{
490 "default_policy": "deny",
491 }
492
493 policyBytes, err := json.Marshal(policyJSON)
494 if err != nil {
495 t.Fatalf("Failed to marshal policy: %v", err)
496 }
497
498 policy, err := New(policyBytes)
499 if err != nil {
500 t.Fatalf("Failed to create policy: %v", err)
501 }
502
503 // Event without specific rule should be denied
504 event := createTestEvent(t, allowedSigner, "test event", 999)
505 allowed, err := policy.CheckPolicy("write", event, allowedSigner.Pub(), "127.0.0.1")
506 if err != nil {
507 t.Errorf("Unexpected error: %v", err)
508 }
509 if allowed {
510 t.Error("FAIL: Event should be denied with default_policy=deny")
511 } else {
512 t.Log("PASS: Event denied with default_policy=deny")
513 }
514 })
515 }
516