package policy import ( "context" "encoding/json" "os" "path/filepath" "testing" "lol.mleku.dev/chk" "git.mleku.dev/mleku/nostr/encoders/hex" "git.mleku.dev/mleku/nostr/interfaces/signer/p8k" ) // TestPolicyDefinitionOfDone tests all requirements from the GitHub issue // Issue: https://git.nostrdev.com/mleku/next.orly.dev/issues/5 // // Requirements: // 1. Configure relay to accept only certain kind events // 2. Scenario A: Only certain users should be allowed to write events // 3. Scenario B: Only certain users should be allowed to read events // 4. Scenario C: Only users involved in events should be able to read the events (privileged) // 5. Scenario D: Scripting option for complex validation func TestPolicyDefinitionOfDone(t *testing.T) { // Generate test keypairs allowedSigner := p8k.MustNew() if err := allowedSigner.Generate(); chk.E(err) { t.Fatalf("Failed to generate allowed signer: %v", err) } allowedPubkey := allowedSigner.Pub() allowedPubkeyHex := hex.Enc(allowedPubkey) unauthorizedSigner := p8k.MustNew() if err := unauthorizedSigner.Generate(); chk.E(err) { t.Fatalf("Failed to generate unauthorized signer: %v", err) } unauthorizedPubkey := unauthorizedSigner.Pub() unauthorizedPubkeyHex := hex.Enc(unauthorizedPubkey) thirdPartySigner := p8k.MustNew() if err := thirdPartySigner.Generate(); chk.E(err) { t.Fatalf("Failed to generate third party signer: %v", err) } thirdPartyPubkey := thirdPartySigner.Pub() t.Logf("Allowed pubkey: %s", allowedPubkeyHex) t.Logf("Unauthorized pubkey: %s", unauthorizedPubkeyHex) // =================================================================== // Requirement 1: Configure relay to accept only certain kind events // =================================================================== t.Run("Requirement 1: Kind Whitelist", func(t *testing.T) { // Create policy with kind whitelist policyJSON := map[string]interface{}{ "kind": map[string]interface{}{ "whitelist": []int{1, 3, 4}, // Only allow kinds 1, 3, 4 }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Test: Kind 1 should be allowed (in whitelist) event1 := createTestEvent(t, allowedSigner, "kind 1 event", 1) allowed, err := policy.CheckPolicy("write", event1, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Kind 1 should be allowed (in whitelist)") } else { t.Log("PASS: Kind 1 is allowed (in whitelist)") } // Test: Kind 5 should be denied (not in whitelist) event5 := createTestEvent(t, allowedSigner, "kind 5 event", 5) allowed, err = policy.CheckPolicy("write", event5, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Kind 5 should be denied (not in whitelist)") } else { t.Log("PASS: Kind 5 is denied (not in whitelist)") } // Test: Kind 3 should be allowed (in whitelist) event3 := createTestEvent(t, allowedSigner, "kind 3 event", 3) allowed, err = policy.CheckPolicy("write", event3, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Kind 3 should be allowed (in whitelist)") } else { t.Log("PASS: Kind 3 is allowed (in whitelist)") } }) // =================================================================== // Requirement 2: Scenario A - Only certain users can write events // =================================================================== t.Run("Scenario A: Per-Kind Write Access Control", func(t *testing.T) { // Create policy with write_allow for kind 10 policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "10": map[string]interface{}{ "description": "Only allowed user can write kind 10", "write_allow": []string{allowedPubkeyHex}, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Test: Allowed user can write kind 10 event10Allowed := createTestEvent(t, allowedSigner, "kind 10 from allowed user", 10) allowed, err := policy.CheckPolicy("write", event10Allowed, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Allowed user should be able to write kind 10") } else { t.Log("PASS: Allowed user can write kind 10") } // Test: Unauthorized user cannot write kind 10 event10Unauthorized := createTestEvent(t, unauthorizedSigner, "kind 10 from unauthorized user", 10) allowed, err = policy.CheckPolicy("write", event10Unauthorized, unauthorizedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Unauthorized user should NOT be able to write kind 10") } else { t.Log("PASS: Unauthorized user cannot write kind 10") } }) // =================================================================== // Requirement 3: Scenario B - Only certain users can read events // =================================================================== t.Run("Scenario B: Per-Kind Read Access Control", func(t *testing.T) { // Create policy with read_allow for kind 20 policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "20": map[string]interface{}{ "description": "Only allowed user can read kind 20", "read_allow": []string{allowedPubkeyHex}, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Create a kind 20 event (doesn't matter who wrote it) event20 := createTestEvent(t, allowedSigner, "kind 20 event", 20) // Test: Allowed user can read kind 20 allowed, err := policy.CheckPolicy("read", event20, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Allowed user should be able to read kind 20") } else { t.Log("PASS: Allowed user can read kind 20") } // Test: Unauthorized user cannot read kind 20 allowed, err = policy.CheckPolicy("read", event20, unauthorizedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Unauthorized user should NOT be able to read kind 20") } else { t.Log("PASS: Unauthorized user cannot read kind 20") } // Test: Unauthenticated user cannot read kind 20 allowed, err = policy.CheckPolicy("read", event20, nil, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Unauthenticated user should NOT be able to read kind 20") } else { t.Log("PASS: Unauthenticated user cannot read kind 20") } }) // =================================================================== // Requirement 4: Scenario C - Only users involved in events can read (privileged) // =================================================================== t.Run("Scenario C: Privileged Events - Only Parties Involved", func(t *testing.T) { // Create policy with privileged flag for kind 30 policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "30": map[string]interface{}{ "description": "Privileged - only parties involved can read", "privileged": true, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Test 1: Author can read their own event event30Author := createTestEvent(t, allowedSigner, "kind 30 authored by allowed user", 30) allowed, err := policy.CheckPolicy("read", event30Author, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Author should be able to read their own privileged event") } else { t.Log("PASS: Author can read their own privileged event") } // Test 2: User in p-tag can read the event event30WithPTag := createTestEvent(t, allowedSigner, "kind 30 with unauthorized in p-tag", 30) addPTag(event30WithPTag, unauthorizedPubkey) // Add unauthorized user to p-tag allowed, err = policy.CheckPolicy("read", event30WithPTag, unauthorizedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: User in p-tag should be able to read privileged event") } else { t.Log("PASS: User in p-tag can read privileged event") } // Test 3: Third party (not author, not in p-tag) cannot read event30NoAccess := createTestEvent(t, allowedSigner, "kind 30 for allowed only", 30) allowed, err = policy.CheckPolicy("read", event30NoAccess, thirdPartyPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Third party should NOT be able to read privileged event") } else { t.Log("PASS: Third party cannot read privileged event") } // Test 4: Unauthenticated user cannot read privileged event allowed, err = policy.CheckPolicy("read", event30NoAccess, nil, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Unauthenticated user should NOT be able to read privileged event") } else { t.Log("PASS: Unauthenticated user cannot read privileged event") } }) // =================================================================== // Requirement 5: Scenario D - Scripting support // =================================================================== t.Run("Scenario D: Scripting Support", func(t *testing.T) { // Create temporary directory for test tempDir := t.TempDir() scriptPath := filepath.Join(tempDir, "test-policy.sh") // Create a simple test script that accepts events with content "accept" scriptContent := `#!/bin/bash while IFS= read -r line; do if echo "$line" | grep -q '"content":"accept"'; then echo '{"id":"test","action":"accept","msg":"accepted by script"}' else echo '{"id":"test","action":"reject","msg":"rejected by script"}' fi done ` if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { t.Fatalf("Failed to write test script: %v", err) } // Create policy with script policyJSON := map[string]interface{}{ "rules": map[string]interface{}{ "40": map[string]interface{}{ "description": "Script-based validation", "script": scriptPath, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Initialize policy manager ctx, cancel := context.WithCancel(context.Background()) defer cancel() policy.manager = &PolicyManager{ ctx: ctx, cancel: cancel, configDir: tempDir, scriptPath: scriptPath, enabled: true, runners: make(map[string]*ScriptRunner), } // Test: Event with "accept" content should be accepted eventAccept := createTestEvent(t, allowedSigner, "accept", 40) allowed, err := policy.CheckPolicy("write", eventAccept, allowedPubkey, "127.0.0.1") if err != nil { t.Logf("Script check failed (expected if script not running): %v", err) t.Log("SKIP: Script execution requires policy manager to be fully running") } else if !allowed { t.Log("INFO: Script rejected event (may be expected if script not running)") } else { t.Log("PASS: Script accepted event with 'accept' content") } // Note: Full script testing requires the policy manager to be running, // which is tested in policy_integration_test.go t.Log("INFO: Full script validation tested in integration tests") }) // =================================================================== // Combined Scenarios // =================================================================== t.Run("Combined: Kind Whitelist + Write Access + Privileged", func(t *testing.T) { // Create comprehensive policy policyJSON := map[string]interface{}{ "kind": map[string]interface{}{ "whitelist": []int{50, 51}, // Only kinds 50 and 51 }, "rules": map[string]interface{}{ "50": map[string]interface{}{ "description": "Write-restricted kind", "write_allow": []string{allowedPubkeyHex}, }, "51": map[string]interface{}{ "description": "Privileged kind", "privileged": true, }, }, } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Test 1: Kind 50 with allowed user should pass event50Allowed := createTestEvent(t, allowedSigner, "kind 50 allowed", 50) allowed, err := policy.CheckPolicy("write", event50Allowed, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Kind 50 with allowed user should pass") } else { t.Log("PASS: Kind 50 with allowed user passes") } // Test 2: Kind 50 with unauthorized user should fail event50Unauthorized := createTestEvent(t, unauthorizedSigner, "kind 50 unauthorized", 50) allowed, err = policy.CheckPolicy("write", event50Unauthorized, unauthorizedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Kind 50 with unauthorized user should fail") } else { t.Log("PASS: Kind 50 with unauthorized user fails") } // Test 3: Kind 100 (not in whitelist) should fail regardless of user event100 := createTestEvent(t, allowedSigner, "kind 100 not in whitelist", 100) allowed, err = policy.CheckPolicy("write", event100, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Kind 100 (not in whitelist) should fail") } else { t.Log("PASS: Kind 100 (not in whitelist) fails") } // Test 4: Kind 51 (privileged) - author can write event51Author := createTestEvent(t, allowedSigner, "kind 51 by author", 51) allowed, err = policy.CheckPolicy("write", event51Author, allowedPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Author should be able to write their own privileged event") } else { t.Log("PASS: Author can write their own privileged event") } // Test 5: Kind 51 (privileged) - third party cannot read allowed, err = policy.CheckPolicy("read", event51Author, thirdPartyPubkey, "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Third party should NOT be able to read privileged event") } else { t.Log("PASS: Third party cannot read privileged event") } }) } // TestDefaultPolicy tests the default_policy configuration func TestDefaultPolicy(t *testing.T) { allowedSigner := p8k.MustNew() if err := allowedSigner.Generate(); chk.E(err) { t.Fatalf("Failed to generate signer: %v", err) } t.Run("default policy allow", func(t *testing.T) { policyJSON := map[string]interface{}{ "default_policy": "allow", } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Event without specific rule should be allowed event := createTestEvent(t, allowedSigner, "test event", 999) allowed, err := policy.CheckPolicy("write", event, allowedSigner.Pub(), "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if !allowed { t.Error("FAIL: Event should be allowed with default_policy=allow") } else { t.Log("PASS: Event allowed with default_policy=allow") } }) t.Run("default policy deny", func(t *testing.T) { policyJSON := map[string]interface{}{ "default_policy": "deny", } policyBytes, err := json.Marshal(policyJSON) if err != nil { t.Fatalf("Failed to marshal policy: %v", err) } policy, err := New(policyBytes) if err != nil { t.Fatalf("Failed to create policy: %v", err) } // Event without specific rule should be denied event := createTestEvent(t, allowedSigner, "test event", 999) allowed, err := policy.CheckPolicy("write", event, allowedSigner.Pub(), "127.0.0.1") if err != nil { t.Errorf("Unexpected error: %v", err) } if allowed { t.Error("FAIL: Event should be denied with default_policy=deny") } else { t.Log("PASS: Event denied with default_policy=deny") } }) }