diff --git a/CLAUDE.md b/CLAUDE.md index 0de3067..2770760 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -99,15 +99,21 @@ type StoredValue struct { ### Configuration Architecture -The system uses feature toggles extensively (`types/Config:271-276`): +The system uses feature toggles extensively (`types/Config:271-280`): ```yaml auth_enabled: true # JWT authentication system tamper_logging_enabled: true # Cryptographic audit trail clustering_enabled: true # Gossip protocol and sync rate_limiting_enabled: true # Per-client rate limiting revision_history_enabled: true # Automatic versioning + +# Anonymous access control (Issue #5 - when auth_enabled: true) +allow_anonymous_read: false # Allow unauthenticated read access to KV endpoints +allow_anonymous_write: false # Allow unauthenticated write access to KV endpoints ``` +**Security Note**: DELETE operations always require authentication when `auth_enabled: true`, regardless of anonymous access settings. + ### Testing Strategy #### Integration Test Suite (`integration_test.sh`) @@ -115,6 +121,11 @@ revision_history_enabled: true # Automatic versioning - **Basic functionality** - Single-node CRUD operations - **Cluster formation** - 2-node gossip protocol and data replication - **Conflict resolution** - Automated conflict detection and resolution using `test_conflict.go` +- **Authentication middleware** - Comprehensive security testing (Issue #4): + - Admin endpoints properly reject unauthenticated requests + - Admin endpoints work with valid JWT tokens + - KV endpoints respect anonymous access configuration + - Automatic root account creation and token extraction The test suite uses sophisticated retry logic and timing to handle the eventually consistent nature of the system. diff --git a/README.md b/README.md index 9760853..d58f4dd 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,10 @@ clustering_enabled: true # Gossip protocol and sync rate_limiting_enabled: true # Rate limiting revision_history_enabled: true # Automatic versioning +# Anonymous access control (when auth_enabled: true) +allow_anonymous_read: false # Allow unauthenticated read access to KV endpoints +allow_anonymous_write: false # Allow unauthenticated write access to KV endpoints + # Backup configuration backup_enabled: true # Automated backups backup_schedule: "0 0 * * *" # Daily at midnight (cron format) @@ -134,7 +138,7 @@ backup_retention: 7 # Days to keep backups ```bash PUT /kv/{path} Content-Type: application/json -Authorization: Bearer # Required if auth_enabled +Authorization: Bearer # Required if auth_enabled && !allow_anonymous_write # Basic storage curl -X PUT http://localhost:8080/kv/users/john/profile \ @@ -158,7 +162,7 @@ curl -X PUT http://localhost:8080/kv/cache/session/abc123 \ #### Retrieve Data ```bash GET /kv/{path} -Authorization: Bearer # Required if auth_enabled +Authorization: Bearer # Required if auth_enabled && !allow_anonymous_read curl -H "Authorization: Bearer eyJ..." http://localhost:8080/kv/users/john/profile @@ -177,7 +181,7 @@ curl -H "Authorization: Bearer eyJ..." http://localhost:8080/kv/users/john/profi #### Delete Data ```bash DELETE /kv/{path} -Authorization: Bearer # Required if auth_enabled +Authorization: Bearer # Always required when auth_enabled (no anonymous delete) curl -X DELETE -H "Authorization: Bearer eyJ..." http://localhost:8080/kv/users/john/profile # Returns: 204 No Content @@ -532,6 +536,8 @@ type StoredValue struct { | `bootstrap_max_age_hours` | Max historical data to sync | 720 hours | 30 days default | | **Feature Toggles** | | `auth_enabled` | JWT authentication system | true | Complete auth/authz system | +| `allow_anonymous_read` | Allow unauthenticated read access | false | When auth_enabled, controls KV GET endpoints | +| `allow_anonymous_write` | Allow unauthenticated write access | false | When auth_enabled, controls KV PUT endpoints | | `clustering_enabled` | Gossip protocol and sync | true | Distributed mode | | `compression_enabled` | ZSTD compression | true | Reduces storage size | | `rate_limiting_enabled` | Rate limiting | true | Per-client limits | diff --git a/integration_test.sh b/integration_test.sh index 4e849a3..e209cf3 100755 --- a/integration_test.sh +++ b/integration_test.sh @@ -361,6 +361,79 @@ EOF fi } +# Test 5: Authentication middleware (Issue #4) +test_authentication_middleware() { + test_start "Authentication middleware test (Issue #4)" + + # Create auth test config + cat > auth_test.yaml <auth_test.log 2>&1 & + local pid=$! + + if wait_for_service 8095; then + sleep 2 # Allow root account creation + + # Extract the token from logs + local token=$(grep "Token:" auth_test.log | sed 's/.*Token: //' | tr -d '\n\r') + + if [ -z "$token" ]; then + log_error "Failed to extract authentication token from logs" + kill $pid 2>/dev/null || true + return + fi + + # Test 1: Admin endpoints should fail without authentication + local no_auth_response=$(curl -s -X POST http://localhost:8095/api/users -H "Content-Type: application/json" -d '{"nickname":"test","password":"test"}') + if echo "$no_auth_response" | grep -q "Unauthorized"; then + log_success "Admin endpoints properly reject unauthenticated requests" + else + log_error "Admin endpoints should reject unauthenticated requests, got: $no_auth_response" + fi + + # Test 2: Admin endpoints should work with valid authentication + local auth_response=$(curl -s -X POST http://localhost:8095/api/users -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d '{"nickname":"authtest","password":"authtest"}') + if echo "$auth_response" | grep -q "uuid"; then + log_success "Admin endpoints work with valid authentication" + else + log_error "Admin endpoints should work with authentication, got: $auth_response" + fi + + # Test 3: KV endpoints should require auth when anonymous access is disabled + local kv_no_auth=$(curl -s -X PUT http://localhost:8095/kv/test/auth -H "Content-Type: application/json" -d '{"test":"auth"}') + if echo "$kv_no_auth" | grep -q "Unauthorized"; then + log_success "KV endpoints properly require authentication when anonymous access disabled" + else + log_error "KV endpoints should require auth when anonymous access disabled, got: $kv_no_auth" + fi + + # Test 4: KV endpoints should work with valid authentication + local kv_auth=$(curl -s -X PUT http://localhost:8095/kv/test/auth -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d '{"test":"auth"}') + if echo "$kv_auth" | grep -q "uuid\|timestamp" || [ -z "$kv_auth" ]; then + log_success "KV endpoints work with valid authentication" + else + log_error "KV endpoints should work with authentication, got: $kv_auth" + fi + + kill $pid 2>/dev/null || true + sleep 2 + else + log_error "Auth test node failed to start" + kill $pid 2>/dev/null || true + fi +} + # Main test execution main() { echo "==================================================" @@ -378,6 +451,7 @@ main() { test_basic_functionality test_cluster_formation test_conflict_resolution + test_authentication_middleware # Results echo "=================================================="