Files
c-relay/tests/white_black_list_test.sh
2025-09-27 13:32:21 -04:00

1054 lines
37 KiB
Bash
Executable File

#!/bin/bash
# =======================================================================
# C-Relay Whitelist/Blacklist Authentication Rules Test Script
# =======================================================================
#
# This test validates the whitelist and blacklist functionality of the
# C-Relay server through the WebSocket admin API.
#
# Test Credentials (Test Mode):
# - Admin Private Key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# - Admin Public Key: 6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3
# - Relay Public Key: 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
#
# =======================================================================
set -e # Exit on any error
# =======================================================================
# CONFIGURATION
# =======================================================================
# Test mode credentials (from current relay startup)
ADMIN_PRIVKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
ADMIN_PUBKEY="6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3"
RELAY_PUBKEY="4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"
# Server configuration
RELAY_HOST="127.0.0.1"
RELAY_PORT="8888"
RELAY_URL="ws://${RELAY_HOST}:${RELAY_PORT}"
# Test configuration
TIMEOUT=5
TEMP_DIR="/tmp/c_relay_test_$$"
# WebSocket connection state (simplified - no persistent connections)
# These variables are kept for compatibility but not used
WS_CONNECTED=0
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
RESET='\033[0m'
# Test tracking
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
# =======================================================================
# UTILITY FUNCTIONS
# =======================================================================
log() {
echo -e "${BLUE}[$(date '+%H:%M:%S')]${RESET} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${RESET} $1"
}
log_error() {
echo -e "${RED}[ERROR]${RESET} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${RESET} $1"
}
log_info() {
echo -e "${BLUE}[INFO]${RESET} $1"
}
increment_test() {
TESTS_RUN=$((TESTS_RUN + 1))
}
pass_test() {
TESTS_PASSED=$((TESTS_PASSED + 1))
log_success "Test $TESTS_RUN: PASSED - $1"
echo ""
echo ""
}
fail_test() {
TESTS_FAILED=$((TESTS_FAILED + 1))
log_error "Test $TESTS_RUN: FAILED - $1"
echo ""
echo ""
}
# Generate test keypairs
generate_test_keypair() {
local name=$1
local privkey_file="${TEMP_DIR}/${name}_privkey"
local pubkey_file="${TEMP_DIR}/${name}_pubkey"
# Generate private key using nak key --gen (following pattern from other tests)
local privkey=$(nak key generate 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$privkey" ]; then
log_error "Failed to generate private key for $name"
return 1
fi
echo "$privkey" > "$privkey_file"
# Derive public key using nak
local pubkey=$(nak key public "$privkey" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$pubkey" ]; then
log_error "Failed to generate public key for $name"
return 1
fi
echo "$pubkey" > "$pubkey_file"
log_info "Generated keypair for $name: pubkey=$pubkey"
# Export for use in calling functions
eval "${name}_PRIVKEY=\"$privkey\""
eval "${name}_PUBKEY=\"$pubkey\""
}
# NIP-44 encryption helper function using nak
encrypt_nip44_content() {
local content="$1"
local sender_privkey="$2"
local receiver_pubkey="$3"
if [ -z "$content" ] || [ -z "$sender_privkey" ] || [ -z "$receiver_pubkey" ]; then
log_error "encrypt_nip44_content: missing required parameters"
return 1
fi
log_info "DEBUG: About to encrypt content: '$content'" >&2
log_info "DEBUG: Sender privkey: $sender_privkey" >&2
log_info "DEBUG: Receiver pubkey: $receiver_pubkey" >&2
# Use nak to perform NIP-44 encryption with correct syntax:
# nak encrypt --recipient-pubkey <pubkey> --sec <private_key> [plaintext]
local encrypted_content
encrypted_content=$(nak encrypt --recipient-pubkey "$receiver_pubkey" --sec "$sender_privkey" "$content" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$encrypted_content" ]; then
log_error "Failed to encrypt content with NIP-44"
log_error "Content: $content"
log_error "Sender privkey: $sender_privkey"
log_error "Receiver pubkey: $receiver_pubkey"
return 1
fi
# Validate that encrypted content is valid base64 and doesn't contain problematic characters
if ! echo "$encrypted_content" | grep -q '^[A-Za-z0-9+/]*=*$'; then
log_error "Encrypted content contains invalid characters for JSON: $encrypted_content"
return 1
fi
# Check if encrypted content is valid UTF-8/base64
if ! echo "$encrypted_content" | base64 -d >/dev/null 2>&1; then
log_warning "Encrypted content may not be valid base64: $encrypted_content"
fi
log_info "DEBUG: Encrypted content: $encrypted_content" >&2
log_info "Successfully encrypted content with NIP-44" >&2
echo "$encrypted_content"
return 0
}
# NIP-44 decryption helper function using nak
decrypt_nip44_content() {
local encrypted_content="$1"
local receiver_privkey="$2"
local sender_pubkey="$3"
if [ -z "$encrypted_content" ] || [ -z "$receiver_privkey" ] || [ -z "$sender_pubkey" ]; then
log_error "decrypt_nip44_content: missing required parameters"
return 1
fi
log_info "DEBUG: Decrypting content: $encrypted_content"
# Use nak to perform NIP-44 decryption with correct syntax:
# nak decrypt --sender-pubkey <pubkey> --sec <private_key> [encrypted_content]
local decrypted_content
decrypted_content=$(nak decrypt --sender-pubkey "$sender_pubkey" --sec "$receiver_privkey" "$encrypted_content" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$decrypted_content" ]; then
log_error "Failed to decrypt content with NIP-44"
log_error "Encrypted content: $encrypted_content"
log_error "Receiver privkey: $receiver_privkey"
log_error "Sender pubkey: $sender_pubkey"
return 1
fi
log_info "DEBUG: Decrypted content: $decrypted_content"
log_info "Successfully decrypted content with NIP-44"
echo "$decrypted_content"
return 0
}
# Send WebSocket message and capture response (simplified pattern from 1_nip_test.sh)
send_websocket_message() {
local message="$1"
local timeout="${2:-$TIMEOUT}"
# Use websocat to send message and capture response (following pattern from tests/1_nip_test.sh)
local response=""
if command -v websocat &> /dev/null; then
response=$(printf '%s\n' "$message" | timeout "$timeout" websocat "$RELAY_URL" 2>&1 || echo "Connection failed")
# Check if connection failed
if [[ "$response" == *"Connection failed"* ]]; then
log_error "Failed to connect to relay"
return 1
fi
else
log_error "websocat not found - required for WebSocket testing"
log_error "Please install websocat for WebSocket communication"
return 1
fi
echo "$response"
}
# Send admin event and capture response (simplified pattern from 1_nip_test.sh)
send_admin_event() {
local event_json="$1"
local description="$2"
local timeout_seconds="${3:-10}"
log_info "Sending admin event: $description"
# Create EVENT message using jq to properly handle special characters
local event_message
event_message=$(jq -n --argjson event "$event_json" '["EVENT", $event]')
# Validate that the event message is valid UTF-8 (temporarily disabled for debugging)
# if ! echo "$event_message" | iconv -f utf-8 -t utf-8 >/dev/null 2>&1; then
# log_error "Event message contains invalid UTF-8 characters"
# return 1
# fi
# Use websocat to send event and capture OK response
local response=""
if command -v websocat &> /dev/null; then
log_info "Sending event using websocat..."
# Debug: Show what we're sending
log_info "DEBUG: Event message being sent: $event_message"
# Write to temporary file to avoid shell interpretation issues
local temp_file="${TEMP_DIR}/event_message_$$"
printf '%s\n' "$event_message" > "$temp_file"
# Send via websocat using file input with delay to receive response
response=$(timeout "$timeout_seconds" sh -c "cat '$temp_file'; sleep 0.5" | websocat "$RELAY_URL" 2>&1)
local websocat_exit_code=$?
# Clean up temp file
rm -f "$temp_file"
log_info "DEBUG: Websocat exit code: $websocat_exit_code"
log_info "DEBUG: Websocat response: $response"
# Check for specific websocat errors
if [[ "$response" == *"UTF-8 failure"* ]]; then
log_error "UTF-8 encoding error in event data for $description"
log_error "Event message: $event_message"
return 1
elif [[ "$response" == *"Connection failed"* ]] || [[ "$response" == *"Connection refused"* ]] || [[ "$response" == *"timeout"* ]]; then
log_error "Failed to connect to relay for $description"
return 1
elif [[ "$response" == *"error running"* ]]; then
log_error "Websocat error for $description: $response"
return 1
elif [ $websocat_exit_code -eq 0 ]; then
log_info "Event sent successfully via websocat"
else
log_warning "Websocat returned exit code $websocat_exit_code"
fi
else
log_error "websocat not found - required for WebSocket testing"
return 1
fi
echo "$response"
}
# Send admin query and wait for encrypted response
send_admin_query() {
local event_json="$1"
local description="$2"
local timeout_seconds="${3:-15}"
log_info "Sending admin query: $description"
# Create EVENT message using jq to properly handle special characters
local event_message
event_message=$(jq -n --argjson event "$event_json" '["EVENT", $event]')
# For queries, we need to also send a REQ to get the response
local sub_id="admin_query_$(date +%s)"
local req_message="[\"REQ\",\"$sub_id\",{\"kinds\":[23456],\"authors\":[\"$RELAY_PUBKEY\"],\"#p\":[\"$ADMIN_PUBKEY\"]}]"
local close_message="[\"CLOSE\",\"$sub_id\"]"
# Send query event and subscription in sequence
local response=""
if command -v websocat &> /dev/null; then
response=$(printf '%s\n%s\n%s\n' "$event_message" "$req_message" "$close_message" | timeout "$timeout_seconds" websocat "$RELAY_URL" 2>&1 || echo "Connection failed")
# Check if connection failed
if [[ "$response" == *"Connection failed"* ]]; then
log_error "Failed to connect to relay for $description"
return 1
fi
# Look for EVENT responses that might contain encrypted query data
local event_response=$(echo "$response" | grep '"EVENT"' | tail -1)
if [ -n "$event_response" ]; then
# Extract the event JSON from the EVENT message
local event_json=$(echo "$event_response" | jq -r '.[2]' 2>/dev/null)
if [ -n "$event_json" ] && [ "$event_json" != "null" ]; then
# Check if this is a kind 23456 response event
local event_kind=$(echo "$event_json" | jq -r '.kind' 2>/dev/null)
if [ "$event_kind" = "23456" ]; then
# Extract encrypted content and decrypt it
local encrypted_content=$(echo "$event_json" | jq -r '.content' 2>/dev/null)
if [ -n "$encrypted_content" ] && [ "$encrypted_content" != "null" ]; then
# Decrypt the response using NIP-44
local decrypted_content
decrypted_content=$(decrypt_nip44_content "$encrypted_content" "$ADMIN_PRIVKEY" "$RELAY_PUBKEY")
if [ $? -eq 0 ] && [ -n "$decrypted_content" ]; then
log_info "Successfully decrypted query response"
echo "$decrypted_content"
return 0
else
log_warning "Failed to decrypt query response content"
fi
fi
fi
fi
fi
else
log_error "websocat not found - required for WebSocket testing"
return 1
fi
# Return the raw response if no encrypted content found
echo "$response"
}
# Create and send auth rule event
send_auth_rule_event() {
local action="$1" # "add" or "remove"
local rule_type="$2" # "whitelist" or "blacklist"
local pattern_type="$3" # "pubkey" or "hash"
local pattern_value="$4" # actual pubkey or hash value
local description="$5" # optional description
log_info "Creating auth rule event: $action $rule_type $pattern_type $pattern_value"
# Create command array according to README.md API specification
# Format: ["blacklist", "pubkey", "abc123..."] or ["whitelist", "pubkey", "def456..."]
local command_array="[\"$rule_type\", \"$pattern_type\", \"$pattern_value\"]"
# Encrypt the command content using NIP-44
local encrypted_content
encrypted_content=$(encrypt_nip44_content "$command_array" "$ADMIN_PRIVKEY" "$RELAY_PUBKEY")
if [ $? -ne 0 ] || [ -z "$encrypted_content" ]; then
log_error "Failed to encrypt auth rule command content"
return 1
fi
# Create the auth rule event using nak with NIP-44 encrypted content
# Using Kind 23456 (admin commands) with proper relay targeting and encrypted content
local event_json
event_json=$(nak event -k 23456 --content "$encrypted_content" \
-t "p=$RELAY_PUBKEY" \
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$event_json" ]; then
log_error "Failed to create auth rule event with nak"
return 1
fi
log_info "DEBUG: Created event JSON: $event_json"
# Send the event using simplified WebSocket pattern
log_info "Publishing auth rule event to relay..."
local result
result=$(send_admin_event "$event_json" "auth rule $action")
local exit_code=$?
log_info "Auth rule event result: $result"
# Check if response indicates success
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i '"OK".*true'; then
log_success "Auth rule $action successful"
return 0
else
log_error "Auth rule $action failed: $result (exit code: $exit_code)"
return 1
fi
}
# Clear all auth rules using the new system command functionality
clear_all_auth_rules() {
log_info "Clearing all existing auth rules..."
# Create command array according to README.md API specification
# Format: ["system_command", "clear_all_auth_rules"]
local command_array="[\"system_command\", \"clear_all_auth_rules\"]"
log_info "DEBUG: Command array: $command_array"
# Encrypt the command content using NIP-44
local encrypted_content
encrypted_content=$(encrypt_nip44_content "$command_array" "$ADMIN_PRIVKEY" "$RELAY_PUBKEY")
if [ $? -ne 0 ] || [ -z "$encrypted_content" ]; then
log_error "Failed to encrypt system command content"
return 1
fi
log_info "DEBUG: Encrypted content: $encrypted_content"
# Create system command event to clear all auth rules
# Using Kind 23456 (admin commands) with proper relay targeting and encrypted content
local event_json
event_json=$(nak event -k 23456 --content "$encrypted_content" \
-t "p=$RELAY_PUBKEY" \
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$event_json" ]; then
log_error "Failed to create clear auth rules event with nak"
return 1
fi
log_info "DEBUG: Created event JSON: $event_json"
# Send the event using simplified WebSocket pattern
log_info "Sending clear all auth rules command..."
local result
result=$(send_admin_event "$event_json" "clear all auth rules")
local exit_code=$?
log_info "Clear auth rules result: $result"
# Check if response indicates success
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i '"OK".*true'; then
log_success "All auth rules cleared successfully"
return 0
else
log_error "Failed to clear auth rules: $result (exit code: $exit_code)"
return 1
fi
}
# Test event publishing with a specific key
test_event_publishing() {
local test_privkey="$1"
local test_pubkey="$2"
local expected_result="$3" # "success" or "blocked"
local description="$4"
log_info "Testing event publishing: $description"
# Create a simple test event (kind 1 - text note) using nak like NIP-42 test
local test_content="Test message from $test_pubkey at $(date)"
local test_event
test_event=$(nak event -k 1 --content "$test_content" --sec "$test_privkey" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$test_event" ]; then
log_error "Failed to create test event"
return 1
fi
# Send the event using nak directly (more reliable than websocat)
log_info "Publishing test event to relay..."
local result
result=$(printf '%s\n' "$test_event" | timeout 10s nak event "$RELAY_URL" 2>&1)
local exit_code=$?
log_info "Event publishing result: $result"
# Check result against expectation
if [ "$expected_result" = "success" ]; then
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i "success\|OK.*true\|published"; then
log_success "Event publishing allowed as expected"
return 0
else
log_error "Event publishing was blocked but should have been allowed: $result"
return 1
fi
else # expected_result = "blocked"
if [ $exit_code -ne 0 ] || echo "$result" | grep -q -i "blocked\|denied\|rejected\|auth.*required\|OK.*false"; then
log_success "Event publishing blocked as expected"
return 0
else
log_error "Event publishing was allowed but should have been blocked: $result"
return 1
fi
fi
}
# =======================================================================
# SETUP AND INITIALIZATION
# =======================================================================
setup_test_environment() {
log "Setting up test environment..."
# Create temporary directory
mkdir -p "$TEMP_DIR"
# Check if required tools are available - like NIP-42 test
log_info "Checking dependencies..."
if ! command -v nak &> /dev/null; then
log_error "nak client not found. Please install: go install github.com/fiatjaf/nak@latest"
exit 1
fi
if ! command -v jq &> /dev/null; then
log_error "jq not found. Please install jq for JSON processing"
exit 1
fi
if ! command -v timeout &> /dev/null; then
log_error "timeout not found. Please install coreutils"
exit 1
fi
if ! command -v websocat &> /dev/null; then
log_error "websocat not found - required for WebSocket testing"
log_error "Please install websocat for WebSocket communication"
exit 1
fi
log_success "Dependencies check complete"
# Generate test keypairs
generate_test_keypair "TEST1"
generate_test_keypair "TEST2"
generate_test_keypair "TEST3"
log_success "Test environment setup complete"
}
# =======================================================================
# TEST FUNCTIONS
# =======================================================================
# Test 1: Admin Authentication
test_admin_authentication() {
increment_test
log "Test $TESTS_RUN: Admin Authentication"
# Create a simple configuration event to test admin authentication
# Using Kind 23456 (admin commands) with NIP-44 encrypted content
# Format: ["system_command", "system_status"]
local command_array="[\"system_command\", \"system_status\"]"
# Encrypt the command content using NIP-44
local encrypted_content
encrypted_content=$(encrypt_nip44_content "$command_array" "$ADMIN_PRIVKEY" "$RELAY_PUBKEY")
if [ $? -ne 0 ] || [ -z "$encrypted_content" ]; then
fail_test "Failed to encrypt admin authentication test content"
return
fi
local config_event
config_event=$(nak event -k 23456 --content "$encrypted_content" \
-t "p=$RELAY_PUBKEY" \
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
if [ $? -ne 0 ]; then
fail_test "Failed to create admin test event"
return
fi
# Send admin event using the proper admin event function
local response
response=$(send_admin_event "$config_event" "admin authentication test")
local exit_code=$?
log_info "Admin authentication result: $response"
if [ $exit_code -eq 0 ] && echo "$response" | grep -q '"OK".*true'; then
pass_test "Admin authentication successful"
else
fail_test "Admin authentication failed: $response (exit code: $exit_code)"
fi
}
# Test 2: Auth Rules Storage and Query Test
test_auth_rules_storage_query() {
increment_test
log "Test $TESTS_RUN: Auth Rules Storage and Query Test"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Add a simple blacklist rule
log_info "Adding test blacklist rule..."
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST1_PUBKEY" "Test storage blacklist entry"; then
log_success "Auth rule added successfully"
# Wait a moment for rule to be processed
sleep 1
# Query all auth rules using admin query
log_info "Querying all auth rules..."
# Create command array according to README.md API specification
# Format: ["auth_query", "all"]
local command_array="[\"auth_query\", \"all\"]"
# Encrypt the command content using NIP-44
local encrypted_content
encrypted_content=$(encrypt_nip44_content "$command_array" "$ADMIN_PRIVKEY" "$RELAY_PUBKEY")
if [ $? -ne 0 ] || [ -z "$encrypted_content" ]; then
fail_test "Failed to encrypt auth query content"
return
fi
local query_event
query_event=$(nak event -k 23456 --content "$encrypted_content" \
-t "p=$RELAY_PUBKEY" \
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$query_event" ]; then
fail_test "Failed to create auth query event"
return
fi
# Send the query event using simplified WebSocket pattern
log_info "Sending auth query to relay..."
local decrypted_response
decrypted_response=$(send_admin_query "$query_event" "auth rules query")
local exit_code=$?
if [ $exit_code -eq 0 ] && [ -n "$decrypted_response" ]; then
log_info "Decrypted query response: $decrypted_response"
# Check if the decrypted response contains our test rule
if echo "$decrypted_response" | grep -q "$TEST1_PUBKEY"; then
pass_test "Auth rule storage and query working - found test rule in decrypted query results"
else
fail_test "Auth rule not found in decrypted query results - rule may not have been stored"
fi
else
fail_test "Failed to receive or decrypt auth query response"
fi
else
fail_test "Failed to add auth rule for storage test"
fi
}
# Test 3: Basic Whitelist Functionality
test_basic_whitelist() {
increment_test
log "Test $TESTS_RUN: Basic Whitelist Functionality"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Add TEST1 pubkey to whitelist
if send_auth_rule_event "add" "whitelist" "pubkey" "$TEST1_PUBKEY" "Test whitelist entry"; then
# Test that whitelisted pubkey can publish
if test_event_publishing "$TEST1_PRIVKEY" "$TEST1_PUBKEY" "success" "whitelisted pubkey"; then
pass_test "Basic whitelist functionality working"
else
fail_test "Whitelisted pubkey could not publish events"
fi
else
fail_test "Failed to add pubkey to whitelist"
fi
}
# Test 4: Basic Blacklist Functionality
test_basic_blacklist() {
increment_test
log "Test $TESTS_RUN: Basic Blacklist Functionality"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Add TEST2 pubkey to blacklist
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST2_PUBKEY" "Test blacklist entry"; then
# Test that blacklisted pubkey cannot publish
if test_event_publishing "$TEST2_PRIVKEY" "$TEST2_PUBKEY" "blocked" "blacklisted pubkey"; then
pass_test "Basic blacklist functionality working"
else
fail_test "Blacklisted pubkey was able to publish events"
fi
else
fail_test "Failed to add pubkey to blacklist"
fi
}
# Test 5: Rule Removal
test_rule_removal() {
increment_test
log "Test $TESTS_RUN: Rule Removal"
# Clear all existing rules to start fresh
clear_all_auth_rules
# First add TEST2 to blacklist to test removal
if ! send_auth_rule_event "add" "blacklist" "pubkey" "$TEST2_PUBKEY" "Test blacklist for removal"; then
fail_test "Failed to add pubkey to blacklist for removal test"
return
fi
# Remove TEST2 from blacklist
if send_auth_rule_event "remove" "blacklist" "pubkey" "$TEST2_PUBKEY" "Remove test blacklist entry"; then
# Test that previously blacklisted pubkey can now publish
if test_event_publishing "$TEST2_PRIVKEY" "$TEST2_PUBKEY" "success" "previously blacklisted pubkey after removal"; then
pass_test "Rule removal working correctly"
else
fail_test "Previously blacklisted pubkey still cannot publish after removal"
fi
else
fail_test "Failed to remove pubkey from blacklist"
fi
}
# Test 6: Multiple Users Scenario
test_multiple_users() {
increment_test
log "Test $TESTS_RUN: Multiple Users Scenario"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Add TEST1 to whitelist and TEST3 to blacklist
local success_count=0
if send_auth_rule_event "add" "whitelist" "pubkey" "$TEST1_PUBKEY" "Multi-user test whitelist"; then
success_count=$((success_count + 1))
fi
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST3_PUBKEY" "Multi-user test blacklist"; then
success_count=$((success_count + 1))
fi
if [ $success_count -eq 2 ]; then
# Test whitelisted user can publish
if test_event_publishing "$TEST1_PRIVKEY" "$TEST1_PUBKEY" "success" "whitelisted in multi-user test"; then
# Test blacklisted user cannot publish
if test_event_publishing "$TEST3_PRIVKEY" "$TEST3_PUBKEY" "blocked" "blacklisted in multi-user test"; then
pass_test "Multiple users scenario working correctly"
else
fail_test "Blacklisted user in multi-user scenario was not blocked"
fi
else
fail_test "Whitelisted user in multi-user scenario was blocked"
fi
else
fail_test "Failed to set up multiple users scenario"
fi
}
# Test 7: Priority Testing (Blacklist vs Whitelist)
test_priority_rules() {
increment_test
log "Test $TESTS_RUN: Priority Rules Testing"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Add same pubkey to both whitelist and blacklist
local setup_success=0
if send_auth_rule_event "add" "whitelist" "pubkey" "$TEST2_PUBKEY" "Priority test whitelist"; then
setup_success=$((setup_success + 1))
fi
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST2_PUBKEY" "Priority test blacklist"; then
setup_success=$((setup_success + 1))
fi
if [ $setup_success -eq 2 ]; then
# Test which rule takes priority (typically blacklist should win)
if test_event_publishing "$TEST2_PRIVKEY" "$TEST2_PUBKEY" "blocked" "pubkey in both whitelist and blacklist"; then
pass_test "Priority rules working correctly (blacklist takes precedence)"
else
# If whitelist wins, that's also valid depending on implementation
log_warning "Whitelist took precedence over blacklist - this may be implementation-specific"
pass_test "Priority rules working (whitelist precedence)"
fi
else
fail_test "Failed to set up priority rules test"
fi
}
# Test 8: Hash-based Blacklist
test_hash_blacklist() {
increment_test
log "Test $TESTS_RUN: Hash-based Blacklist"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Create a test event to get its hash
local test_content="Content to be blacklisted by hash"
local test_event
test_event=$(nak event -k 1 --content "$test_content" --sec "$TEST1_PRIVKEY" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$test_event" ]; then
fail_test "Failed to create test event for hash blacklist"
return
fi
# Extract event ID (hash) from the event using jq
local event_id
event_id=$(echo "$test_event" | jq -r '.id' 2>/dev/null)
if [ -z "$event_id" ] || [ "$event_id" = "null" ]; then
fail_test "Failed to extract event ID for hash blacklist test"
return
fi
log_info "Testing hash blacklist with event ID: $event_id"
# Add the event ID to hash blacklist
if send_auth_rule_event "add" "blacklist" "hash" "$event_id" "Test hash blacklist"; then
# Try to publish the same event using nak - should be blocked
log_info "Attempting to publish blacklisted event..."
local result
result=$(printf '%s\n' "$test_event" | timeout 10s nak event "$RELAY_URL" 2>&1)
local exit_code=$?
if [ $exit_code -ne 0 ] || echo "$result" | grep -q -i "blocked\|denied\|rejected\|blacklist"; then
pass_test "Hash-based blacklist working correctly"
else
fail_test "Hash-based blacklist did not block the event: $result"
fi
else
fail_test "Failed to add event hash to blacklist"
fi
}
# Test 9: WebSocket Connection Behavior
test_websocket_behavior() {
increment_test
log "Test $TESTS_RUN: WebSocket Connection Behavior"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Test that the WebSocket connection handles multiple rapid requests
local rapid_success_count=0
for i in {1..3}; do
local test_content="Rapid test message $i"
local test_event
test_event=$(nak event -k 1 --content "$test_content" --sec "$TEST1_PRIVKEY" 2>/dev/null)
if [ $? -eq 0 ]; then
local message="[\"EVENT\",$test_event]"
local response
response=$(send_websocket_message "$message" 5)
if echo "$response" | grep -q '"OK"'; then
rapid_success_count=$((rapid_success_count + 1))
fi
fi
# Small delay between requests
sleep 0.1
done
if [ $rapid_success_count -ge 2 ]; then
pass_test "WebSocket connection handles multiple requests correctly"
else
fail_test "WebSocket connection failed to handle multiple rapid requests ($rapid_success_count/3 succeeded)"
fi
}
# Test 10: Rule Persistence Verification
test_rule_persistence() {
increment_test
log "Test $TESTS_RUN: Rule Persistence Verification"
# Clear all existing rules to start fresh
clear_all_auth_rules
# Add a rule, then verify it persists by testing enforcement
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST3_PUBKEY" "Persistence test blacklist"; then
# Wait a moment for rule to be processed
sleep 1
# Test enforcement multiple times to verify persistence
local enforcement_count=0
for i in {1..2}; do
if test_event_publishing "$TEST3_PRIVKEY" "$TEST3_PUBKEY" "blocked" "persistence test attempt $i"; then
enforcement_count=$((enforcement_count + 1))
fi
sleep 0.5
done
if [ $enforcement_count -eq 2 ]; then
pass_test "Rule persistence working correctly"
else
fail_test "Rule persistence failed ($enforcement_count/2 enforcements succeeded)"
fi
else
fail_test "Failed to add rule for persistence test"
fi
}
# Test 11: Cleanup and Final Verification
test_cleanup_verification() {
increment_test
log "Test $TESTS_RUN: Cleanup and Final Verification"
# Remove all test rules
local cleanup_success=0
# Remove whitelist entries
if send_auth_rule_event "remove" "whitelist" "pubkey" "$TEST1_PUBKEY" "Cleanup whitelist"; then
cleanup_success=$((cleanup_success + 1))
fi
# Remove blacklist entries
for pubkey in "$TEST2_PUBKEY" "$TEST3_PUBKEY"; do
if send_auth_rule_event "remove" "blacklist" "pubkey" "$pubkey" "Cleanup blacklist"; then
cleanup_success=$((cleanup_success + 1))
fi
done
if [ $cleanup_success -ge 2 ]; then
# Verify that previously restricted pubkeys can now publish
if test_event_publishing "$TEST3_PRIVKEY" "$TEST3_PUBKEY" "success" "after cleanup verification"; then
pass_test "Cleanup and verification successful"
else
log_warning "Cleanup completed but restrictions may still be active"
pass_test "Cleanup completed (partial verification)"
fi
else
fail_test "Cleanup failed ($cleanup_success rules removed)"
fi
}
# =======================================================================
# MAIN TEST EXECUTION
# =======================================================================
run_all_tests() {
log "Starting comprehensive whitelist/blacklist functionality tests..."
# Setup
setup_test_environment
clear_all_auth_rules
test_admin_authentication
# test_auth_rules_storage_query
# test_basic_whitelist
# test_basic_blacklist
# test_rule_removal
# test_multiple_users
# test_priority_rules
# test_hash_blacklist
# test_websocket_behavior
# test_rule_persistence
# test_cleanup_verification
# Test summary
echo ""
echo -e "${BOLD}=== TEST SUMMARY ===${RESET}"
echo -e "Tests run: ${BLUE}$TESTS_RUN${RESET}"
echo -e "Tests passed: ${GREEN}$TESTS_PASSED${RESET}"
echo -e "Tests failed: ${RED}$TESTS_FAILED${RESET}"
echo ""
if [ $TESTS_FAILED -eq 0 ]; then
log_success "All tests passed! Whitelist/blacklist functionality is working correctly."
return 0
else
log_error "$TESTS_FAILED out of $TESTS_RUN tests failed."
return 1
fi
}
# =======================================================================
# CLEANUP FUNCTIONS
# =======================================================================
cleanup() {
log "Cleaning up test environment..."
# Remove temporary directory
if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
rm -rf "$TEMP_DIR"
log_info "Temporary directory removed: $TEMP_DIR"
fi
log "Test cleanup completed."
}
# Set up cleanup trap
trap cleanup EXIT
# =======================================================================
# SCRIPT ENTRY POINT
# =======================================================================
main() {
echo -e "${BOLD}${BLUE}C-Relay Whitelist/Blacklist Authentication Test${RESET}"
echo -e "${BLUE}===============================================${RESET}"
echo ""
# Check if relay is running - using websocat like the working tests
if ! printf '%s\n' '["REQ","connection_test",{}]' | timeout 5 websocat "$RELAY_URL" >/dev/null 2>&1; then
log_error "Cannot connect to relay at $RELAY_URL"
log_error "Please ensure the C-Relay server is running in test mode"
exit 1
fi
log_success "Connected to relay at $RELAY_URL"
# Run all tests
if run_all_tests; then
echo ""
log_success "All whitelist/blacklist tests completed successfully!"
exit 0
else
echo ""
log_error "Some tests failed."
exit 1
fi
}
# Run main function if script is executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi