Implement policy-based event filtering and add integration tests
- Enhanced the HandleReq function to incorporate policy checks for privileged events, ensuring only authorized users can access sensitive data. - Introduced a new integration test suite for policy filtering, validating the behavior of event access based on user authentication and policy rules. - Added a script to automate the policy filter integration tests, improving testing efficiency and reliability. - Updated version to v0.20.2 to reflect the new features and improvements.
This commit is contained in:
516
pkg/policy/policy_integration_test.go
Normal file
516
pkg/policy/policy_integration_test.go
Normal file
@@ -0,0 +1,516 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/crypto/p256k"
|
||||
"next.orly.dev/pkg/encoders/event"
|
||||
"next.orly.dev/pkg/encoders/hex"
|
||||
"next.orly.dev/pkg/encoders/kind"
|
||||
"next.orly.dev/pkg/encoders/tag"
|
||||
)
|
||||
|
||||
// TestPolicyIntegration runs the relay with policy enabled and tests event filtering
|
||||
func TestPolicyIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
// Generate test keys
|
||||
allowedSigner := &p256k.Signer{}
|
||||
if err := allowedSigner.Generate(); chk.E(err) {
|
||||
t.Fatalf("Failed to generate allowed signer: %v", err)
|
||||
}
|
||||
allowedPubkeyHex := hex.Enc(allowedSigner.Pub())
|
||||
|
||||
unauthorizedSigner := &p256k.Signer{}
|
||||
if err := unauthorizedSigner.Generate(); chk.E(err) {
|
||||
t.Fatalf("Failed to generate unauthorized signer: %v", err)
|
||||
}
|
||||
|
||||
// Create temporary directory for policy config
|
||||
tempDir := t.TempDir()
|
||||
configDir := filepath.Join(tempDir, "ORLY_TEST")
|
||||
if err := os.MkdirAll(configDir, 0755); chk.E(err) {
|
||||
t.Fatalf("Failed to create config directory: %v", err)
|
||||
}
|
||||
|
||||
// Create policy JSON with generated keys
|
||||
policyJSON := map[string]interface{}{
|
||||
"kind": map[string]interface{}{
|
||||
"whitelist": []int{4678, 10306, 30520, 30919},
|
||||
},
|
||||
"rules": map[string]interface{}{
|
||||
"4678": map[string]interface{}{
|
||||
"description": "Zenotp message events",
|
||||
"script": filepath.Join(configDir, "validate4678.js"), // Won't exist, should fall back to default
|
||||
"privileged": true,
|
||||
},
|
||||
"10306": map[string]interface{}{
|
||||
"description": "End user whitelist changes",
|
||||
"read_allow": []string{allowedPubkeyHex},
|
||||
"privileged": true,
|
||||
},
|
||||
"30520": map[string]interface{}{
|
||||
"description": "Zenotp events",
|
||||
"write_allow": []string{allowedPubkeyHex},
|
||||
"privileged": true,
|
||||
},
|
||||
"30919": map[string]interface{}{
|
||||
"description": "Zenotp events",
|
||||
"write_allow": []string{allowedPubkeyHex},
|
||||
"privileged": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
policyJSONBytes, err := json.MarshalIndent(policyJSON, "", " ")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal policy JSON: %v", err)
|
||||
}
|
||||
|
||||
policyPath := filepath.Join(configDir, "policy.json")
|
||||
if err := os.WriteFile(policyPath, policyJSONBytes, 0644); chk.E(err) {
|
||||
t.Fatalf("Failed to write policy file: %v", err)
|
||||
}
|
||||
|
||||
// Create events with proper signatures
|
||||
// Event 1: Kind 30520 with allowed pubkey (should be allowed)
|
||||
event30520Allowed := event.New()
|
||||
event30520Allowed.CreatedAt = time.Now().Unix()
|
||||
event30520Allowed.Kind = kind.K{K: 30520}.K
|
||||
event30520Allowed.Content = []byte("test event 30520")
|
||||
event30520Allowed.Tags = tag.NewS()
|
||||
addPTag(event30520Allowed, allowedSigner.Pub()) // Add p tag for privileged check
|
||||
if err := event30520Allowed.Sign(allowedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign event30520Allowed: %v", err)
|
||||
}
|
||||
|
||||
// Event 2: Kind 30520 with unauthorized pubkey (should be denied)
|
||||
event30520Unauthorized := event.New()
|
||||
event30520Unauthorized.CreatedAt = time.Now().Unix()
|
||||
event30520Unauthorized.Kind = kind.K{K: 30520}.K
|
||||
event30520Unauthorized.Content = []byte("test event 30520 unauthorized")
|
||||
event30520Unauthorized.Tags = tag.NewS()
|
||||
if err := event30520Unauthorized.Sign(unauthorizedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign event30520Unauthorized: %v", err)
|
||||
}
|
||||
|
||||
// Event 3: Kind 10306 with allowed pubkey (should be readable by allowed user)
|
||||
event10306Allowed := event.New()
|
||||
event10306Allowed.CreatedAt = time.Now().Unix()
|
||||
event10306Allowed.Kind = kind.K{K: 10306}.K
|
||||
event10306Allowed.Content = []byte("test event 10306")
|
||||
event10306Allowed.Tags = tag.NewS()
|
||||
addPTag(event10306Allowed, allowedSigner.Pub()) // Add p tag for privileged check
|
||||
if err := event10306Allowed.Sign(allowedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign event10306Allowed: %v", err)
|
||||
}
|
||||
|
||||
// Event 4: Kind 4678 with allowed pubkey (script-based, should fall back to default)
|
||||
event4678Allowed := event.New()
|
||||
event4678Allowed.CreatedAt = time.Now().Unix()
|
||||
event4678Allowed.Kind = kind.K{K: 4678}.K
|
||||
event4678Allowed.Content = []byte("test event 4678")
|
||||
event4678Allowed.Tags = tag.NewS()
|
||||
addPTag(event4678Allowed, allowedSigner.Pub()) // Add p tag for privileged check
|
||||
if err := event4678Allowed.Sign(allowedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign event4678Allowed: %v", err)
|
||||
}
|
||||
|
||||
// Test policy loading
|
||||
policy, err := New(policyJSONBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create policy: %v", err)
|
||||
}
|
||||
|
||||
// Verify policy loaded correctly
|
||||
if len(policy.Rules) != 4 {
|
||||
t.Errorf("Expected 4 rules, got %d", len(policy.Rules))
|
||||
}
|
||||
|
||||
// Test policy checks directly
|
||||
t.Run("policy checks", func(t *testing.T) {
|
||||
// Test 1: Event 30520 with allowed pubkey should be allowed
|
||||
allowed, err := policy.CheckPolicy("write", event30520Allowed, allowedSigner.Pub(), "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event30520Allowed to be allowed")
|
||||
}
|
||||
|
||||
// Test 2: Event 30520 with unauthorized pubkey should be denied
|
||||
allowed, err = policy.CheckPolicy("write", event30520Unauthorized, unauthorizedSigner.Pub(), "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event30520Unauthorized to be denied")
|
||||
}
|
||||
|
||||
// Test 3: Event 10306 should be readable by allowed user
|
||||
allowed, err = policy.CheckPolicy("read", event10306Allowed, allowedSigner.Pub(), "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event10306Allowed to be readable by allowed user")
|
||||
}
|
||||
|
||||
// Test 4: Event 10306 should NOT be readable by unauthorized user
|
||||
allowed, err = policy.CheckPolicy("read", event10306Allowed, unauthorizedSigner.Pub(), "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event10306Allowed to be denied for unauthorized user")
|
||||
}
|
||||
|
||||
// Test 5: Event 10306 should NOT be readable without authentication
|
||||
allowed, err = policy.CheckPolicy("read", event10306Allowed, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event10306Allowed to be denied without authentication (privileged)")
|
||||
}
|
||||
|
||||
// Test 6: Event 30520 should NOT be writable without authentication
|
||||
allowed, err = policy.CheckPolicy("write", event30520Allowed, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event30520Allowed to be denied without authentication (privileged)")
|
||||
}
|
||||
|
||||
// Test 7: Event 4678 should fall back to default policy (allow) when script not running
|
||||
allowed, err = policy.CheckPolicy("write", event4678Allowed, allowedSigner.Pub(), "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event4678Allowed to be allowed when script not running (falls back to default)")
|
||||
}
|
||||
|
||||
// Test 8: Event 4678 should be denied without authentication (privileged check)
|
||||
allowed, err = policy.CheckPolicy("write", event4678Allowed, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event4678Allowed to be denied without authentication (privileged)")
|
||||
}
|
||||
})
|
||||
|
||||
// Test with relay simulation (checking log output)
|
||||
t.Run("relay simulation", func(t *testing.T) {
|
||||
// Note: We can't easily capture log output in tests, so we just verify
|
||||
// that policy checks work correctly
|
||||
|
||||
// Simulate policy checks that would happen in relay
|
||||
// First, publish events (simulate write checks)
|
||||
checks := []struct {
|
||||
name string
|
||||
event *event.E
|
||||
loggedInPubkey []byte
|
||||
access string
|
||||
shouldAllow bool
|
||||
shouldLog string // Expected log message substring, empty means no specific log expected
|
||||
}{
|
||||
{
|
||||
name: "write 30520 with allowed pubkey",
|
||||
event: event30520Allowed,
|
||||
loggedInPubkey: allowedSigner.Pub(),
|
||||
access: "write",
|
||||
shouldAllow: true,
|
||||
},
|
||||
{
|
||||
name: "write 30520 with unauthorized pubkey",
|
||||
event: event30520Unauthorized,
|
||||
loggedInPubkey: unauthorizedSigner.Pub(),
|
||||
access: "write",
|
||||
shouldAllow: false,
|
||||
},
|
||||
{
|
||||
name: "read 10306 with allowed pubkey",
|
||||
event: event10306Allowed,
|
||||
loggedInPubkey: allowedSigner.Pub(),
|
||||
access: "read",
|
||||
shouldAllow: true,
|
||||
},
|
||||
{
|
||||
name: "read 10306 with unauthorized pubkey",
|
||||
event: event10306Allowed,
|
||||
loggedInPubkey: unauthorizedSigner.Pub(),
|
||||
access: "read",
|
||||
shouldAllow: false,
|
||||
},
|
||||
{
|
||||
name: "read 10306 without authentication",
|
||||
event: event10306Allowed,
|
||||
loggedInPubkey: nil,
|
||||
access: "read",
|
||||
shouldAllow: false,
|
||||
},
|
||||
{
|
||||
name: "write 30520 without authentication",
|
||||
event: event30520Allowed,
|
||||
loggedInPubkey: nil,
|
||||
access: "write",
|
||||
shouldAllow: false,
|
||||
},
|
||||
{
|
||||
name: "write 4678 with allowed pubkey",
|
||||
event: event4678Allowed,
|
||||
loggedInPubkey: allowedSigner.Pub(),
|
||||
access: "write",
|
||||
shouldAllow: true,
|
||||
shouldLog: "", // Should not log "policy rule is inactive" if script is not configured
|
||||
},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
t.Run(check.name, func(t *testing.T) {
|
||||
allowed, err := policy.CheckPolicy(check.access, check.event, check.loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
if allowed != check.shouldAllow {
|
||||
t.Errorf("Expected allowed=%v, got %v", check.shouldAllow, allowed)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Test event IDs are regenerated correctly after signing
|
||||
t.Run("event ID regeneration", func(t *testing.T) {
|
||||
// Create a new event, sign it, then verify ID is correct
|
||||
testEvent := event.New()
|
||||
testEvent.CreatedAt = time.Now().Unix()
|
||||
testEvent.Kind = kind.K{K: 30520}.K
|
||||
testEvent.Content = []byte("test content")
|
||||
testEvent.Tags = tag.NewS()
|
||||
|
||||
// Sign the event
|
||||
if err := testEvent.Sign(allowedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign test event: %v", err)
|
||||
}
|
||||
|
||||
// Verify event ID is correct (should be SHA256 of serialized event)
|
||||
if len(testEvent.ID) != 32 {
|
||||
t.Errorf("Expected event ID to be 32 bytes, got %d", len(testEvent.ID))
|
||||
}
|
||||
|
||||
// Verify signature is correct
|
||||
if len(testEvent.Sig) != 64 {
|
||||
t.Errorf("Expected event signature to be 64 bytes, got %d", len(testEvent.Sig))
|
||||
}
|
||||
|
||||
// Verify signature validates using event's Verify method
|
||||
valid, err := testEvent.Verify()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to verify signature: %v", err)
|
||||
}
|
||||
if !valid {
|
||||
t.Error("Event signature verification failed")
|
||||
}
|
||||
})
|
||||
|
||||
// Test WebSocket client simulation (for future integration)
|
||||
t.Run("websocket client simulation", func(t *testing.T) {
|
||||
// This test simulates what would happen if we connected via WebSocket
|
||||
// For now, we'll just verify the events can be serialized correctly
|
||||
|
||||
events := []*event.E{
|
||||
event30520Allowed,
|
||||
event30520Unauthorized,
|
||||
event10306Allowed,
|
||||
event4678Allowed,
|
||||
}
|
||||
|
||||
for i, ev := range events {
|
||||
t.Run(fmt.Sprintf("event_%d", i), func(t *testing.T) {
|
||||
// Serialize event
|
||||
serialized := ev.Serialize()
|
||||
if len(serialized) == 0 {
|
||||
t.Error("Event serialization returned empty")
|
||||
}
|
||||
|
||||
// Verify event can be parsed back (simplified check)
|
||||
if len(ev.ID) != 32 {
|
||||
t.Errorf("Event ID length incorrect: %d", len(ev.ID))
|
||||
}
|
||||
if len(ev.Pubkey) != 32 {
|
||||
t.Errorf("Event pubkey length incorrect: %d", len(ev.Pubkey))
|
||||
}
|
||||
if len(ev.Sig) != 64 {
|
||||
t.Errorf("Event signature length incorrect: %d", len(ev.Sig))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestPolicyWithRelay creates a comprehensive test that simulates relay behavior
|
||||
func TestPolicyWithRelay(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
allowedSigner := &p256k.Signer{}
|
||||
if err := allowedSigner.Generate(); chk.E(err) {
|
||||
t.Fatalf("Failed to generate allowed signer: %v", err)
|
||||
}
|
||||
allowedPubkeyHex := hex.Enc(allowedSigner.Pub())
|
||||
|
||||
unauthorizedSigner := &p256k.Signer{}
|
||||
if err := unauthorizedSigner.Generate(); chk.E(err) {
|
||||
t.Fatalf("Failed to generate unauthorized signer: %v", err)
|
||||
}
|
||||
|
||||
// Create policy JSON
|
||||
policyJSON := map[string]interface{}{
|
||||
"kind": map[string]interface{}{
|
||||
"whitelist": []int{4678, 10306, 30520, 30919},
|
||||
},
|
||||
"rules": map[string]interface{}{
|
||||
"10306": map[string]interface{}{
|
||||
"description": "End user whitelist changes",
|
||||
"read_allow": []string{allowedPubkeyHex},
|
||||
"privileged": true,
|
||||
},
|
||||
"30520": map[string]interface{}{
|
||||
"description": "Zenotp events",
|
||||
"write_allow": []string{allowedPubkeyHex},
|
||||
"privileged": true,
|
||||
},
|
||||
"30919": map[string]interface{}{
|
||||
"description": "Zenotp events",
|
||||
"write_allow": []string{allowedPubkeyHex},
|
||||
"privileged": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
policyJSONBytes, err := json.Marshal(policyJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal policy JSON: %v", err)
|
||||
}
|
||||
|
||||
policy, err := New(policyJSONBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create policy: %v", err)
|
||||
}
|
||||
|
||||
// Create test event (kind 30520) with allowed pubkey
|
||||
testEvent := event.New()
|
||||
testEvent.CreatedAt = time.Now().Unix()
|
||||
testEvent.Kind = kind.K{K: 30520}.K
|
||||
testEvent.Content = []byte("test content")
|
||||
testEvent.Tags = tag.NewS()
|
||||
addPTag(testEvent, allowedSigner.Pub())
|
||||
if err := testEvent.Sign(allowedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign test event: %v", err)
|
||||
}
|
||||
|
||||
// Test scenarios
|
||||
scenarios := []struct {
|
||||
name string
|
||||
loggedInPubkey []byte
|
||||
expectedResult bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "authenticated as allowed pubkey",
|
||||
loggedInPubkey: allowedSigner.Pub(),
|
||||
expectedResult: true,
|
||||
description: "Should allow when authenticated as allowed pubkey",
|
||||
},
|
||||
{
|
||||
name: "unauthenticated",
|
||||
loggedInPubkey: nil,
|
||||
expectedResult: false,
|
||||
description: "Should deny when not authenticated (privileged check)",
|
||||
},
|
||||
{
|
||||
name: "authenticated as different pubkey",
|
||||
loggedInPubkey: unauthorizedSigner.Pub(),
|
||||
expectedResult: false,
|
||||
description: "Should deny when authenticated as different pubkey",
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
allowed, err := policy.CheckPolicy("write", testEvent, scenario.loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
if allowed != scenario.expectedResult {
|
||||
t.Errorf("%s: Expected allowed=%v, got %v", scenario.description, scenario.expectedResult, allowed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Test read access for kind 10306
|
||||
readEvent := event.New()
|
||||
readEvent.CreatedAt = time.Now().Unix()
|
||||
readEvent.Kind = kind.K{K: 10306}.K
|
||||
readEvent.Content = []byte("test read event")
|
||||
readEvent.Tags = tag.NewS()
|
||||
addPTag(readEvent, allowedSigner.Pub())
|
||||
if err := readEvent.Sign(allowedSigner); chk.E(err) {
|
||||
t.Fatalf("Failed to sign read event: %v", err)
|
||||
}
|
||||
|
||||
readScenarios := []struct {
|
||||
name string
|
||||
loggedInPubkey []byte
|
||||
expectedResult bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "read authenticated as allowed pubkey",
|
||||
loggedInPubkey: allowedSigner.Pub(),
|
||||
expectedResult: true,
|
||||
description: "Should allow read when authenticated as allowed pubkey",
|
||||
},
|
||||
{
|
||||
name: "read unauthenticated",
|
||||
loggedInPubkey: nil,
|
||||
expectedResult: false,
|
||||
description: "Should deny read when not authenticated (privileged check)",
|
||||
},
|
||||
{
|
||||
name: "read authenticated as different pubkey",
|
||||
loggedInPubkey: unauthorizedSigner.Pub(),
|
||||
expectedResult: false,
|
||||
description: "Should deny read when authenticated as different pubkey",
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range readScenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
allowed, err := policy.CheckPolicy("read", readEvent, scenario.loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
if allowed != scenario.expectedResult {
|
||||
t.Errorf("%s: Expected allowed=%v, got %v", scenario.description, scenario.expectedResult, allowed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,3 +1495,363 @@ func TestDefaultPolicyLogicWithRules(t *testing.T) {
|
||||
t.Error("Expected kind 2 to be allowed (no rule, default policy is allow)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyFilterProcessing(t *testing.T) {
|
||||
// Test policy filter processing using the provided filter JSON specification
|
||||
filterJSON := []byte(`{
|
||||
"kind": {
|
||||
"whitelist": [4678, 10306, 30520, 30919]
|
||||
},
|
||||
"rules": {
|
||||
"4678": {
|
||||
"description": "Zenotp message events",
|
||||
"script": "/home/orly/.config/ORLY/validate4678.js",
|
||||
"privileged": true
|
||||
},
|
||||
"10306": {
|
||||
"description": "End user whitelist changes",
|
||||
"read_allow": [
|
||||
"04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
],
|
||||
"privileged": true
|
||||
},
|
||||
"30520": {
|
||||
"description": "Zenotp events",
|
||||
"write_allow": [
|
||||
"04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
],
|
||||
"privileged": true
|
||||
},
|
||||
"30919": {
|
||||
"description": "Zenotp events",
|
||||
"write_allow": [
|
||||
"04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
],
|
||||
"privileged": true
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
// Create policy from JSON
|
||||
policy, err := New(filterJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create policy from JSON: %v", err)
|
||||
}
|
||||
|
||||
// Verify whitelist is set correctly
|
||||
if len(policy.Kind.Whitelist) != 4 {
|
||||
t.Errorf("Expected whitelist to have 4 kinds, got %d", len(policy.Kind.Whitelist))
|
||||
}
|
||||
expectedKinds := map[int]bool{4678: true, 10306: true, 30520: true, 30919: true}
|
||||
for _, kind := range policy.Kind.Whitelist {
|
||||
if !expectedKinds[kind] {
|
||||
t.Errorf("Unexpected kind in whitelist: %d", kind)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify rules are loaded correctly
|
||||
if len(policy.Rules) != 4 {
|
||||
t.Errorf("Expected 4 rules, got %d", len(policy.Rules))
|
||||
}
|
||||
|
||||
// Verify rule 4678 (script-based)
|
||||
rule4678, ok := policy.Rules[4678]
|
||||
if !ok {
|
||||
t.Fatal("Rule 4678 not found")
|
||||
}
|
||||
if rule4678.Description != "Zenotp message events" {
|
||||
t.Errorf("Expected description 'Zenotp message events', got '%s'", rule4678.Description)
|
||||
}
|
||||
if rule4678.Script != "/home/orly/.config/ORLY/validate4678.js" {
|
||||
t.Errorf("Expected script path '/home/orly/.config/ORLY/validate4678.js', got '%s'", rule4678.Script)
|
||||
}
|
||||
if !rule4678.Privileged {
|
||||
t.Error("Expected rule 4678 to be privileged")
|
||||
}
|
||||
|
||||
// Verify rule 10306 (read_allow)
|
||||
rule10306, ok := policy.Rules[10306]
|
||||
if !ok {
|
||||
t.Fatal("Rule 10306 not found")
|
||||
}
|
||||
if rule10306.Description != "End user whitelist changes" {
|
||||
t.Errorf("Expected description 'End user whitelist changes', got '%s'", rule10306.Description)
|
||||
}
|
||||
if len(rule10306.ReadAllow) != 1 {
|
||||
t.Errorf("Expected 1 read_allow pubkey, got %d", len(rule10306.ReadAllow))
|
||||
}
|
||||
if rule10306.ReadAllow[0] != "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5" {
|
||||
t.Errorf("Expected read_allow pubkey '04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5', got '%s'", rule10306.ReadAllow[0])
|
||||
}
|
||||
if !rule10306.Privileged {
|
||||
t.Error("Expected rule 10306 to be privileged")
|
||||
}
|
||||
|
||||
// Verify rule 30520 (write_allow)
|
||||
rule30520, ok := policy.Rules[30520]
|
||||
if !ok {
|
||||
t.Fatal("Rule 30520 not found")
|
||||
}
|
||||
if rule30520.Description != "Zenotp events" {
|
||||
t.Errorf("Expected description 'Zenotp events', got '%s'", rule30520.Description)
|
||||
}
|
||||
if len(rule30520.WriteAllow) != 1 {
|
||||
t.Errorf("Expected 1 write_allow pubkey, got %d", len(rule30520.WriteAllow))
|
||||
}
|
||||
if rule30520.WriteAllow[0] != "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5" {
|
||||
t.Errorf("Expected write_allow pubkey '04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5', got '%s'", rule30520.WriteAllow[0])
|
||||
}
|
||||
if !rule30520.Privileged {
|
||||
t.Error("Expected rule 30520 to be privileged")
|
||||
}
|
||||
|
||||
// Verify rule 30919 (write_allow)
|
||||
rule30919, ok := policy.Rules[30919]
|
||||
if !ok {
|
||||
t.Fatal("Rule 30919 not found")
|
||||
}
|
||||
if rule30919.Description != "Zenotp events" {
|
||||
t.Errorf("Expected description 'Zenotp events', got '%s'", rule30919.Description)
|
||||
}
|
||||
if len(rule30919.WriteAllow) != 1 {
|
||||
t.Errorf("Expected 1 write_allow pubkey, got %d", len(rule30919.WriteAllow))
|
||||
}
|
||||
if rule30919.WriteAllow[0] != "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5" {
|
||||
t.Errorf("Expected write_allow pubkey '04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5', got '%s'", rule30919.WriteAllow[0])
|
||||
}
|
||||
if !rule30919.Privileged {
|
||||
t.Error("Expected rule 30919 to be privileged")
|
||||
}
|
||||
|
||||
// Test kind whitelist filtering
|
||||
t.Run("kind whitelist filtering", func(t *testing.T) {
|
||||
eventSigner, eventPubkey := generateTestKeypair(t)
|
||||
_, loggedInPubkey := generateTestKeypair(t)
|
||||
|
||||
// Test whitelisted kind (should pass kind check, but may fail other checks)
|
||||
whitelistedEvent := createTestEvent(t, eventSigner, "test content", 4678)
|
||||
addPTag(whitelistedEvent, loggedInPubkey)
|
||||
allowed, err := policy.CheckPolicy("write", whitelistedEvent, loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
// Should be allowed because script isn't running (falls back to default "allow")
|
||||
// and privileged check passes (loggedInPubkey is in p tag)
|
||||
if !allowed {
|
||||
t.Error("Expected whitelisted kind to be allowed when script not running and privileged check passes")
|
||||
}
|
||||
|
||||
// Test non-whitelisted kind (should be denied)
|
||||
nonWhitelistedEvent := createTestEvent(t, eventSigner, "test content", 1)
|
||||
allowed, err = policy.CheckPolicy("write", nonWhitelistedEvent, eventPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected non-whitelisted kind to be denied")
|
||||
}
|
||||
})
|
||||
|
||||
// Test read access for kind 10306
|
||||
t.Run("read access for kind 10306", func(t *testing.T) {
|
||||
allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode allowed pubkey: %v", err)
|
||||
}
|
||||
|
||||
// Create event with allowed pubkey using a temporary signer
|
||||
// Note: We can't actually sign with just the pubkey, so we'll create the event
|
||||
// with a random signer and then manually set the pubkey for testing
|
||||
tempSigner, _ := generateTestKeypair(t)
|
||||
event10306 := createTestEvent(t, tempSigner, "test content", 10306)
|
||||
event10306.Pubkey = allowedPubkeyBytes
|
||||
// Re-sign won't work without private key, but policy checks use the pubkey field
|
||||
// So we'll test with the pubkey set correctly
|
||||
|
||||
// Test read access with allowed pubkey (logged in user matches event pubkey)
|
||||
allowed, err := policy.CheckPolicy("read", event10306, allowedPubkeyBytes, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event to be allowed for read access with allowed pubkey")
|
||||
}
|
||||
|
||||
// Test read access with non-allowed pubkey
|
||||
_, unauthorizedPubkey := generateTestKeypair(t)
|
||||
allowed, err = policy.CheckPolicy("read", event10306, unauthorizedPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied for read access with non-allowed pubkey")
|
||||
}
|
||||
|
||||
// Test read access without authentication (privileged check)
|
||||
allowed, err = policy.CheckPolicy("read", event10306, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied for read access without authentication (privileged)")
|
||||
}
|
||||
})
|
||||
|
||||
// Test write access for kind 30520
|
||||
t.Run("write access for kind 30520", func(t *testing.T) {
|
||||
allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode allowed pubkey: %v", err)
|
||||
}
|
||||
|
||||
// Create event with allowed pubkey using a temporary signer
|
||||
// We'll set the pubkey manually for testing since we don't have the private key
|
||||
tempSigner, _ := generateTestKeypair(t)
|
||||
event30520 := createTestEvent(t, tempSigner, "test content", 30520)
|
||||
event30520.Pubkey = allowedPubkeyBytes
|
||||
|
||||
// Test write access with allowed pubkey (event pubkey matches write_allow)
|
||||
allowed, err := policy.CheckPolicy("write", event30520, allowedPubkeyBytes, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event to be allowed for write access with allowed pubkey")
|
||||
}
|
||||
|
||||
// Test write access with non-allowed pubkey
|
||||
unauthorizedSigner, unauthorizedPubkey := generateTestKeypair(t)
|
||||
unauthorizedEvent := createTestEvent(t, unauthorizedSigner, "test content", 30520)
|
||||
allowed, err = policy.CheckPolicy("write", unauthorizedEvent, unauthorizedPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied for write access with non-allowed pubkey")
|
||||
}
|
||||
|
||||
// Test write access without authentication (privileged check)
|
||||
allowed, err = policy.CheckPolicy("write", event30520, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied for write access without authentication (privileged)")
|
||||
}
|
||||
})
|
||||
|
||||
// Test write access for kind 30919
|
||||
t.Run("write access for kind 30919", func(t *testing.T) {
|
||||
allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode allowed pubkey: %v", err)
|
||||
}
|
||||
|
||||
// Create event with allowed pubkey using a temporary signer
|
||||
// We'll set the pubkey manually for testing since we don't have the private key
|
||||
tempSigner, _ := generateTestKeypair(t)
|
||||
event30919 := createTestEvent(t, tempSigner, "test content", 30919)
|
||||
event30919.Pubkey = allowedPubkeyBytes
|
||||
|
||||
// Test write access with allowed pubkey (event pubkey matches write_allow)
|
||||
allowed, err := policy.CheckPolicy("write", event30919, allowedPubkeyBytes, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event to be allowed for write access with allowed pubkey")
|
||||
}
|
||||
|
||||
// Test write access with non-allowed pubkey
|
||||
unauthorizedSigner, unauthorizedPubkey := generateTestKeypair(t)
|
||||
unauthorizedEvent := createTestEvent(t, unauthorizedSigner, "test content", 30919)
|
||||
allowed, err = policy.CheckPolicy("write", unauthorizedEvent, unauthorizedPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied for write access with non-allowed pubkey")
|
||||
}
|
||||
|
||||
// Test write access without authentication (privileged check)
|
||||
allowed, err = policy.CheckPolicy("write", event30919, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied for write access without authentication (privileged)")
|
||||
}
|
||||
})
|
||||
|
||||
// Test privileged flag behavior with p tags
|
||||
t.Run("privileged flag with p tags", func(t *testing.T) {
|
||||
eventSigner, _ := generateTestKeypair(t)
|
||||
_, loggedInPubkey := generateTestKeypair(t)
|
||||
allowedPubkeyHex := "04eeb1ed409c0b9205e722f8bf1780f553b61876ef323aff16c9f80a9d8ee9f5"
|
||||
allowedPubkeyBytes, err := hex.Dec(allowedPubkeyHex)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode allowed pubkey: %v", err)
|
||||
}
|
||||
|
||||
// Create event with allowed pubkey and logged-in pubkey in p tag
|
||||
event30520 := createTestEvent(t, eventSigner, "test content", 30520)
|
||||
event30520.Pubkey = allowedPubkeyBytes
|
||||
addPTag(event30520, loggedInPubkey)
|
||||
|
||||
// Test that event is allowed when logged-in pubkey is in p tag (privileged)
|
||||
// and event pubkey matches write_allow
|
||||
allowed, err := policy.CheckPolicy("write", event30520, loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !allowed {
|
||||
t.Error("Expected event to be allowed when event pubkey matches write_allow and logged-in pubkey is in p tag")
|
||||
}
|
||||
|
||||
// Test that event is denied when logged-in pubkey is not in p tag and doesn't match event pubkey
|
||||
event30520NoPTag := createTestEvent(t, eventSigner, "test content", 30520)
|
||||
event30520NoPTag.Pubkey = allowedPubkeyBytes
|
||||
allowed, err = policy.CheckPolicy("write", event30520NoPTag, loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied when logged-in pubkey is not in p tag (privileged check fails)")
|
||||
}
|
||||
})
|
||||
|
||||
// Test script-based rule (kind 4678)
|
||||
t.Run("script-based rule for kind 4678", func(t *testing.T) {
|
||||
eventSigner, _ := generateTestKeypair(t)
|
||||
_, loggedInPubkey := generateTestKeypair(t)
|
||||
|
||||
event4678 := createTestEvent(t, eventSigner, "test content", 4678)
|
||||
addPTag(event4678, loggedInPubkey)
|
||||
|
||||
// Test with script not running (should fall back to default policy)
|
||||
allowed, err := policy.CheckPolicy("write", event4678, loggedInPubkey, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
// Should allow because default policy is "allow" and script is not running
|
||||
// and privileged check passes (loggedInPubkey is in p tag)
|
||||
if !allowed {
|
||||
t.Error("Expected event to be allowed when script is not running (falls back to default 'allow') and privileged check passes")
|
||||
}
|
||||
|
||||
// Test without authentication (privileged check should fail)
|
||||
allowed, err = policy.CheckPolicy("write", event4678, nil, "127.0.0.1")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
// Should be denied because privileged check fails without authentication
|
||||
// The privileged check happens in checkRulePolicy before script check
|
||||
// So it should be denied even though script is not running
|
||||
if allowed {
|
||||
t.Error("Expected event to be denied without authentication (privileged check)")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user