v0.1.7 - Fixing black and white lists
This commit is contained in:
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
BIN
db/ginxsom.db
BIN
db/ginxsom.db
Binary file not shown.
78
db/migrations/001_add_auth_rules.sql
Normal file
78
db/migrations/001_add_auth_rules.sql
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
-- Migration: Add authentication rules tables
|
||||||
|
-- Purpose: Enable whitelist/blacklist functionality for Ginxsom
|
||||||
|
-- Date: 2025-01-12
|
||||||
|
|
||||||
|
-- Enable foreign key constraints
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
|
||||||
|
-- Authentication rules table for whitelist/blacklist functionality
|
||||||
|
CREATE TABLE IF NOT EXISTS auth_rules (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
rule_type TEXT NOT NULL, -- 'pubkey_blacklist', 'pubkey_whitelist',
|
||||||
|
-- 'hash_blacklist', 'mime_blacklist', 'mime_whitelist'
|
||||||
|
rule_target TEXT NOT NULL, -- The pubkey, hash, or MIME type to match
|
||||||
|
operation TEXT NOT NULL DEFAULT '*', -- 'upload', 'delete', 'list', or '*' for all
|
||||||
|
enabled INTEGER NOT NULL DEFAULT 1, -- 1 = enabled, 0 = disabled
|
||||||
|
priority INTEGER NOT NULL DEFAULT 100,-- Lower number = higher priority
|
||||||
|
description TEXT, -- Human-readable description
|
||||||
|
created_by TEXT, -- Admin pubkey who created the rule
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
|
||||||
|
-- Constraints
|
||||||
|
CHECK (rule_type IN ('pubkey_blacklist', 'pubkey_whitelist',
|
||||||
|
'hash_blacklist', 'mime_blacklist', 'mime_whitelist')),
|
||||||
|
CHECK (operation IN ('upload', 'delete', 'list', '*')),
|
||||||
|
CHECK (enabled IN (0, 1)),
|
||||||
|
CHECK (priority >= 0),
|
||||||
|
|
||||||
|
-- Unique constraint: one rule per type/target/operation combination
|
||||||
|
UNIQUE(rule_type, rule_target, operation)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for performance optimization
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_type_target ON auth_rules(rule_type, rule_target);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_operation ON auth_rules(operation);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_enabled ON auth_rules(enabled);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_priority ON auth_rules(priority);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_type_operation ON auth_rules(rule_type, operation, enabled);
|
||||||
|
|
||||||
|
-- Cache table for authentication decisions (5-minute TTL)
|
||||||
|
CREATE TABLE IF NOT EXISTS auth_rules_cache (
|
||||||
|
cache_key TEXT PRIMARY KEY NOT NULL, -- SHA-256 hash of request parameters
|
||||||
|
decision INTEGER NOT NULL, -- 1 = allow, 0 = deny
|
||||||
|
reason TEXT, -- Reason for decision
|
||||||
|
pubkey TEXT, -- Public key from request
|
||||||
|
operation TEXT, -- Operation type
|
||||||
|
resource_hash TEXT, -- Resource hash (if applicable)
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
expires_at INTEGER NOT NULL, -- Expiration timestamp
|
||||||
|
|
||||||
|
CHECK (decision IN (0, 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for cache expiration cleanup
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_cache_expires ON auth_rules_cache(expires_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_cache_pubkey ON auth_rules_cache(pubkey);
|
||||||
|
|
||||||
|
-- Insert example rules (commented out - uncomment to use)
|
||||||
|
-- Example: Blacklist a specific pubkey for uploads
|
||||||
|
-- INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description, created_by) VALUES
|
||||||
|
-- ('pubkey_blacklist', '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 'upload', 10, 'Example blacklisted user', 'admin_pubkey_here');
|
||||||
|
|
||||||
|
-- Example: Whitelist a specific pubkey for all operations
|
||||||
|
-- INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description, created_by) VALUES
|
||||||
|
-- ('pubkey_whitelist', 'your_pubkey_here', '*', 300, 'Trusted user - all operations allowed', 'admin_pubkey_here');
|
||||||
|
|
||||||
|
-- Example: Blacklist executable MIME types
|
||||||
|
-- INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description, created_by) VALUES
|
||||||
|
-- ('mime_blacklist', 'application/x-executable', 'upload', 200, 'Block executable files', 'admin_pubkey_here'),
|
||||||
|
-- ('mime_blacklist', 'application/x-msdos-program', 'upload', 200, 'Block DOS executables', 'admin_pubkey_here'),
|
||||||
|
-- ('mime_blacklist', 'application/x-msdownload', 'upload', 200, 'Block Windows executables', 'admin_pubkey_here');
|
||||||
|
|
||||||
|
-- Example: Whitelist common image types
|
||||||
|
-- INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description, created_by) VALUES
|
||||||
|
-- ('mime_whitelist', 'image/jpeg', 'upload', 400, 'Allow JPEG images', 'admin_pubkey_here'),
|
||||||
|
-- ('mime_whitelist', 'image/png', 'upload', 400, 'Allow PNG images', 'admin_pubkey_here'),
|
||||||
|
-- ('mime_whitelist', 'image/gif', 'upload', 400, 'Allow GIF images', 'admin_pubkey_here'),
|
||||||
|
-- ('mime_whitelist', 'image/webp', 'upload', 400, 'Allow WebP images', 'admin_pubkey_here');
|
||||||
@@ -43,6 +43,56 @@ INSERT OR IGNORE INTO config (key, value, description) VALUES
|
|||||||
('nip42_challenge_timeout', '600', 'NIP-42 challenge timeout in seconds'),
|
('nip42_challenge_timeout', '600', 'NIP-42 challenge timeout in seconds'),
|
||||||
('nip42_time_tolerance', '300', 'NIP-42 timestamp tolerance in seconds');
|
('nip42_time_tolerance', '300', 'NIP-42 timestamp tolerance in seconds');
|
||||||
|
|
||||||
|
-- Authentication rules table for whitelist/blacklist functionality
|
||||||
|
CREATE TABLE IF NOT EXISTS auth_rules (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
rule_type TEXT NOT NULL, -- 'pubkey_blacklist', 'pubkey_whitelist',
|
||||||
|
-- 'hash_blacklist', 'mime_blacklist', 'mime_whitelist'
|
||||||
|
rule_target TEXT NOT NULL, -- The pubkey, hash, or MIME type to match
|
||||||
|
operation TEXT NOT NULL DEFAULT '*', -- 'upload', 'delete', 'list', or '*' for all
|
||||||
|
enabled INTEGER NOT NULL DEFAULT 1, -- 1 = enabled, 0 = disabled
|
||||||
|
priority INTEGER NOT NULL DEFAULT 100,-- Lower number = higher priority
|
||||||
|
description TEXT, -- Human-readable description
|
||||||
|
created_by TEXT, -- Admin pubkey who created the rule
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
|
||||||
|
-- Constraints
|
||||||
|
CHECK (rule_type IN ('pubkey_blacklist', 'pubkey_whitelist',
|
||||||
|
'hash_blacklist', 'mime_blacklist', 'mime_whitelist')),
|
||||||
|
CHECK (operation IN ('upload', 'delete', 'list', '*')),
|
||||||
|
CHECK (enabled IN (0, 1)),
|
||||||
|
CHECK (priority >= 0),
|
||||||
|
|
||||||
|
-- Unique constraint: one rule per type/target/operation combination
|
||||||
|
UNIQUE(rule_type, rule_target, operation)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Cache table for authentication decisions (5-minute TTL)
|
||||||
|
CREATE TABLE IF NOT EXISTS auth_rules_cache (
|
||||||
|
cache_key TEXT PRIMARY KEY NOT NULL, -- SHA-256 hash of request parameters
|
||||||
|
decision INTEGER NOT NULL, -- 1 = allow, 0 = deny
|
||||||
|
reason TEXT, -- Reason for decision
|
||||||
|
pubkey TEXT, -- Public key from request
|
||||||
|
operation TEXT, -- Operation type
|
||||||
|
resource_hash TEXT, -- Resource hash (if applicable)
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
expires_at INTEGER NOT NULL, -- Expiration timestamp
|
||||||
|
|
||||||
|
CHECK (decision IN (0, 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for performance optimization
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_type_target ON auth_rules(rule_type, rule_target);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_operation ON auth_rules(operation);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_enabled ON auth_rules(enabled);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_priority ON auth_rules(priority);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_type_operation ON auth_rules(rule_type, operation, enabled);
|
||||||
|
|
||||||
|
-- Index for cache expiration cleanup
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_cache_expires ON auth_rules_cache(expires_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_cache_pubkey ON auth_rules_cache(pubkey);
|
||||||
|
|
||||||
-- View for storage statistics
|
-- View for storage statistics
|
||||||
CREATE VIEW IF NOT EXISTS storage_stats AS
|
CREATE VIEW IF NOT EXISTS storage_stats AS
|
||||||
SELECT
|
SELECT
|
||||||
|
|||||||
496
docs/AUTH_RULES_IMPLEMENTATION_PLAN.md
Normal file
496
docs/AUTH_RULES_IMPLEMENTATION_PLAN.md
Normal file
@@ -0,0 +1,496 @@
|
|||||||
|
# Authentication Rules Implementation Plan
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This document outlines the implementation plan for adding whitelist/blacklist functionality to the Ginxsom Blossom server. The authentication rules system is **already coded** in [`src/request_validator.c`](src/request_validator.c) but lacks the database schema to function. This plan focuses on completing the implementation by adding the missing database tables and Admin API endpoints.
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
|
||||||
|
### ✅ Already Implemented
|
||||||
|
- **Nostr event validation** - Full cryptographic verification (NIP-42 and Blossom)
|
||||||
|
- **Rule evaluation engine** - Complete priority-based logic in [`check_database_auth_rules()`](src/request_validator.c:1309-1471)
|
||||||
|
- **Configuration system** - `auth_rules_enabled` flag in config table
|
||||||
|
- **Admin API framework** - Authentication and endpoint structure in place
|
||||||
|
- **Documentation** - Comprehensive flow diagrams in [`docs/AUTH_API.md`](docs/AUTH_API.md)
|
||||||
|
|
||||||
|
### ❌ Missing Components
|
||||||
|
- **Database schema** - `auth_rules` table doesn't exist
|
||||||
|
- **Cache table** - `auth_rules_cache` for performance optimization
|
||||||
|
- **Admin API endpoints** - CRUD operations for managing rules
|
||||||
|
- **Migration script** - Database schema updates
|
||||||
|
- **Test suite** - Validation of rule enforcement
|
||||||
|
|
||||||
|
## Database Schema Design
|
||||||
|
|
||||||
|
### 1. auth_rules Table
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Authentication rules for whitelist/blacklist functionality
|
||||||
|
CREATE TABLE IF NOT EXISTS auth_rules (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
rule_type TEXT NOT NULL, -- 'pubkey_blacklist', 'pubkey_whitelist',
|
||||||
|
-- 'hash_blacklist', 'mime_blacklist', 'mime_whitelist'
|
||||||
|
rule_target TEXT NOT NULL, -- The pubkey, hash, or MIME type to match
|
||||||
|
operation TEXT NOT NULL DEFAULT '*', -- 'upload', 'delete', 'list', or '*' for all
|
||||||
|
enabled INTEGER NOT NULL DEFAULT 1, -- 1 = enabled, 0 = disabled
|
||||||
|
priority INTEGER NOT NULL DEFAULT 100,-- Lower number = higher priority
|
||||||
|
description TEXT, -- Human-readable description
|
||||||
|
created_by TEXT, -- Admin pubkey who created the rule
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
|
||||||
|
-- Constraints
|
||||||
|
CHECK (rule_type IN ('pubkey_blacklist', 'pubkey_whitelist',
|
||||||
|
'hash_blacklist', 'mime_blacklist', 'mime_whitelist')),
|
||||||
|
CHECK (operation IN ('upload', 'delete', 'list', '*')),
|
||||||
|
CHECK (enabled IN (0, 1)),
|
||||||
|
CHECK (priority >= 0),
|
||||||
|
|
||||||
|
-- Unique constraint: one rule per type/target/operation combination
|
||||||
|
UNIQUE(rule_type, rule_target, operation)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for performance
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_type_target ON auth_rules(rule_type, rule_target);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_operation ON auth_rules(operation);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_enabled ON auth_rules(enabled);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_rules_priority ON auth_rules(priority);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. auth_rules_cache Table
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Cache for authentication decisions (5-minute TTL)
|
||||||
|
CREATE TABLE IF NOT EXISTS auth_rules_cache (
|
||||||
|
cache_key TEXT PRIMARY KEY NOT NULL, -- SHA-256 hash of request parameters
|
||||||
|
decision INTEGER NOT NULL, -- 1 = allow, 0 = deny
|
||||||
|
reason TEXT, -- Reason for decision
|
||||||
|
pubkey TEXT, -- Public key from request
|
||||||
|
operation TEXT, -- Operation type
|
||||||
|
resource_hash TEXT, -- Resource hash (if applicable)
|
||||||
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
expires_at INTEGER NOT NULL, -- Expiration timestamp
|
||||||
|
|
||||||
|
CHECK (decision IN (0, 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for cache expiration cleanup
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_auth_cache_expires ON auth_rules_cache(expires_at);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Rule Type Definitions
|
||||||
|
|
||||||
|
| Rule Type | Purpose | Target Format | Priority Range |
|
||||||
|
|-----------|---------|---------------|----------------|
|
||||||
|
| `pubkey_blacklist` | Block specific users | 64-char hex pubkey | 1-99 (highest) |
|
||||||
|
| `hash_blacklist` | Block specific files | 64-char hex SHA-256 | 100-199 |
|
||||||
|
| `mime_blacklist` | Block file types | MIME type string | 200-299 |
|
||||||
|
| `pubkey_whitelist` | Allow specific users | 64-char hex pubkey | 300-399 |
|
||||||
|
| `mime_whitelist` | Allow file types | MIME type string | 400-499 |
|
||||||
|
|
||||||
|
### 4. Operation Types
|
||||||
|
|
||||||
|
- `upload` - File upload operations
|
||||||
|
- `delete` - File deletion operations
|
||||||
|
- `list` - File listing operations
|
||||||
|
- `*` - All operations (wildcard)
|
||||||
|
|
||||||
|
## Admin API Endpoints
|
||||||
|
|
||||||
|
### GET /api/rules
|
||||||
|
**Purpose**: List all authentication rules with filtering
|
||||||
|
**Authentication**: Required (admin pubkey)
|
||||||
|
**Query Parameters**:
|
||||||
|
- `rule_type` (optional): Filter by rule type
|
||||||
|
- `operation` (optional): Filter by operation
|
||||||
|
- `enabled` (optional): Filter by enabled status (true/false)
|
||||||
|
- `limit` (default: 100): Number of rules to return
|
||||||
|
- `offset` (default: 0): Pagination offset
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"rule_type": "pubkey_blacklist",
|
||||||
|
"rule_target": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||||
|
"operation": "upload",
|
||||||
|
"enabled": true,
|
||||||
|
"priority": 10,
|
||||||
|
"description": "Blocked spammer account",
|
||||||
|
"created_by": "admin_pubkey_here",
|
||||||
|
"created_at": 1704067200,
|
||||||
|
"updated_at": 1704067200
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"limit": 100,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST /api/rules
|
||||||
|
**Purpose**: Create a new authentication rule
|
||||||
|
**Authentication**: Required (admin pubkey)
|
||||||
|
**Request Body**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"rule_type": "pubkey_blacklist",
|
||||||
|
"rule_target": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||||
|
"operation": "upload",
|
||||||
|
"priority": 10,
|
||||||
|
"description": "Blocked spammer account"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "Rule created successfully",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"rule_type": "pubkey_blacklist",
|
||||||
|
"rule_target": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||||
|
"operation": "upload",
|
||||||
|
"enabled": true,
|
||||||
|
"priority": 10,
|
||||||
|
"description": "Blocked spammer account",
|
||||||
|
"created_at": 1704067200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### PUT /api/rules/:id
|
||||||
|
**Purpose**: Update an existing rule
|
||||||
|
**Authentication**: Required (admin pubkey)
|
||||||
|
**Request Body**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": false,
|
||||||
|
"priority": 20,
|
||||||
|
"description": "Updated description"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "Rule updated successfully",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"updated_fields": ["enabled", "priority", "description"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### DELETE /api/rules/:id
|
||||||
|
**Purpose**: Delete an authentication rule
|
||||||
|
**Authentication**: Required (admin pubkey)
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "Rule deleted successfully",
|
||||||
|
"data": {
|
||||||
|
"id": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST /api/rules/clear-cache
|
||||||
|
**Purpose**: Clear the authentication rules cache
|
||||||
|
**Authentication**: Required (admin pubkey)
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "Authentication cache cleared",
|
||||||
|
"data": {
|
||||||
|
"entries_cleared": 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GET /api/rules/test
|
||||||
|
**Purpose**: Test if a specific request would be allowed
|
||||||
|
**Authentication**: Required (admin pubkey)
|
||||||
|
**Query Parameters**:
|
||||||
|
- `pubkey` (required): Public key to test
|
||||||
|
- `operation` (required): Operation type (upload/delete/list)
|
||||||
|
- `hash` (optional): Resource hash
|
||||||
|
- `mime` (optional): MIME type
|
||||||
|
|
||||||
|
**Response**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"allowed": false,
|
||||||
|
"reason": "Public key blacklisted",
|
||||||
|
"matched_rule": {
|
||||||
|
"id": 1,
|
||||||
|
"rule_type": "pubkey_blacklist",
|
||||||
|
"description": "Blocked spammer account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Database Schema (Priority: HIGH)
|
||||||
|
**Estimated Time**: 2-4 hours
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
1. Create migration script `db/migrations/001_add_auth_rules.sql`
|
||||||
|
2. Add `auth_rules` table with indexes
|
||||||
|
3. Add `auth_rules_cache` table with indexes
|
||||||
|
4. Create migration runner script
|
||||||
|
5. Test migration on clean database
|
||||||
|
6. Test migration on existing database
|
||||||
|
|
||||||
|
**Deliverables**:
|
||||||
|
- Migration SQL script
|
||||||
|
- Migration runner bash script
|
||||||
|
- Migration documentation
|
||||||
|
|
||||||
|
**Validation**:
|
||||||
|
- Verify tables created successfully
|
||||||
|
- Verify indexes exist
|
||||||
|
- Verify constraints work correctly
|
||||||
|
- Test with sample data
|
||||||
|
|
||||||
|
### Phase 2: Admin API Endpoints (Priority: HIGH)
|
||||||
|
**Estimated Time**: 6-8 hours
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
1. Implement `GET /api/rules` endpoint
|
||||||
|
2. Implement `POST /api/rules` endpoint
|
||||||
|
3. Implement `PUT /api/rules/:id` endpoint
|
||||||
|
4. Implement `DELETE /api/rules/:id` endpoint
|
||||||
|
5. Implement `POST /api/rules/clear-cache` endpoint
|
||||||
|
6. Implement `GET /api/rules/test` endpoint
|
||||||
|
7. Add input validation for all endpoints
|
||||||
|
8. Add error handling and logging
|
||||||
|
|
||||||
|
**Deliverables**:
|
||||||
|
- C implementation in `src/admin_api.c`
|
||||||
|
- Header declarations in `src/ginxsom.h`
|
||||||
|
- API documentation updates
|
||||||
|
|
||||||
|
**Validation**:
|
||||||
|
- Test each endpoint with valid data
|
||||||
|
- Test error cases (invalid input, missing auth, etc.)
|
||||||
|
- Verify database operations work correctly
|
||||||
|
- Check response formats match specification
|
||||||
|
|
||||||
|
### Phase 3: Integration & Testing (Priority: HIGH)
|
||||||
|
**Estimated Time**: 4-6 hours
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
1. Create comprehensive test suite
|
||||||
|
2. Test rule creation and enforcement
|
||||||
|
3. Test cache functionality
|
||||||
|
4. Test priority ordering
|
||||||
|
5. Test whitelist default-deny behavior
|
||||||
|
6. Test performance with many rules
|
||||||
|
7. Document test scenarios
|
||||||
|
|
||||||
|
**Deliverables**:
|
||||||
|
- Test script `tests/auth_rules_test.sh`
|
||||||
|
- Performance benchmarks
|
||||||
|
- Test documentation
|
||||||
|
|
||||||
|
**Validation**:
|
||||||
|
- All test cases pass
|
||||||
|
- Performance meets requirements (<3ms per request)
|
||||||
|
- Cache hit rate >80% under load
|
||||||
|
- No memory leaks detected
|
||||||
|
|
||||||
|
### Phase 4: Documentation & Examples (Priority: MEDIUM)
|
||||||
|
**Estimated Time**: 2-3 hours
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
1. Update [`docs/AUTH_API.md`](docs/AUTH_API.md) with rule management
|
||||||
|
2. Create usage examples
|
||||||
|
3. Document common patterns (blocking users, allowing file types)
|
||||||
|
4. Create migration guide for existing deployments
|
||||||
|
5. Add troubleshooting section
|
||||||
|
|
||||||
|
**Deliverables**:
|
||||||
|
- Updated documentation
|
||||||
|
- Example scripts
|
||||||
|
- Migration guide
|
||||||
|
- Troubleshooting guide
|
||||||
|
|
||||||
|
## Code Changes Required
|
||||||
|
|
||||||
|
### 1. src/request_validator.c
|
||||||
|
**Status**: ✅ Already implemented - NO CHANGES NEEDED
|
||||||
|
|
||||||
|
The rule evaluation logic is complete in [`check_database_auth_rules()`](src/request_validator.c:1309-1471). Once the database tables exist, this code will work immediately.
|
||||||
|
|
||||||
|
### 2. src/admin_api.c
|
||||||
|
**Status**: ❌ Needs new endpoints
|
||||||
|
|
||||||
|
Add new functions:
|
||||||
|
```c
|
||||||
|
// Rule management endpoints
|
||||||
|
int handle_get_rules(FCGX_Request *request);
|
||||||
|
int handle_create_rule(FCGX_Request *request);
|
||||||
|
int handle_update_rule(FCGX_Request *request);
|
||||||
|
int handle_delete_rule(FCGX_Request *request);
|
||||||
|
int handle_clear_cache(FCGX_Request *request);
|
||||||
|
int handle_test_rule(FCGX_Request *request);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. src/ginxsom.h
|
||||||
|
**Status**: ❌ Needs new declarations
|
||||||
|
|
||||||
|
Add function prototypes for new admin endpoints.
|
||||||
|
|
||||||
|
### 4. db/schema.sql
|
||||||
|
**Status**: ❌ Needs new tables
|
||||||
|
|
||||||
|
Add `auth_rules` and `auth_rules_cache` table definitions.
|
||||||
|
|
||||||
|
## Migration Strategy
|
||||||
|
|
||||||
|
### For New Installations
|
||||||
|
1. Run updated `db/init.sh` which includes new tables
|
||||||
|
2. No additional steps needed
|
||||||
|
|
||||||
|
### For Existing Installations
|
||||||
|
1. Create backup: `cp db/ginxsom.db db/ginxsom.db.backup`
|
||||||
|
2. Run migration: `sqlite3 db/ginxsom.db < db/migrations/001_add_auth_rules.sql`
|
||||||
|
3. Verify migration: `sqlite3 db/ginxsom.db ".schema auth_rules"`
|
||||||
|
4. Restart server to load new schema
|
||||||
|
|
||||||
|
### Rollback Procedure
|
||||||
|
1. Stop server
|
||||||
|
2. Restore backup: `cp db/ginxsom.db.backup db/ginxsom.db`
|
||||||
|
3. Restart server
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Cache Strategy
|
||||||
|
- **5-minute TTL** balances freshness with performance
|
||||||
|
- **SHA-256 cache keys** prevent collision attacks
|
||||||
|
- **Automatic cleanup** of expired entries every 5 minutes
|
||||||
|
- **Cache hit target**: >80% under normal load
|
||||||
|
|
||||||
|
### Database Optimization
|
||||||
|
- **Indexes on all query columns** for fast lookups
|
||||||
|
- **Prepared statements** prevent SQL injection
|
||||||
|
- **Single connection** with proper cleanup
|
||||||
|
- **Query optimization** for rule evaluation order
|
||||||
|
|
||||||
|
### Expected Performance
|
||||||
|
- **Cache hit**: ~100μs (SQLite SELECT)
|
||||||
|
- **Cache miss**: ~2.4ms (full validation + rule checks)
|
||||||
|
- **Rule creation**: ~50ms (INSERT + cache invalidation)
|
||||||
|
- **Rule update**: ~30ms (UPDATE + cache invalidation)
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Input Validation
|
||||||
|
- Validate all rule_type values against enum
|
||||||
|
- Validate pubkey format (64 hex chars)
|
||||||
|
- Validate hash format (64 hex chars)
|
||||||
|
- Validate MIME type format
|
||||||
|
- Sanitize description text
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
- All rule management requires admin pubkey
|
||||||
|
- Verify Nostr event signatures
|
||||||
|
- Check event expiration
|
||||||
|
- Log all rule changes with admin pubkey
|
||||||
|
|
||||||
|
### Attack Mitigation
|
||||||
|
- **Rule flooding**: Limit total rules per type
|
||||||
|
- **Cache poisoning**: Cryptographic cache keys
|
||||||
|
- **Priority manipulation**: Validate priority ranges
|
||||||
|
- **Whitelist bypass**: Default-deny when whitelist exists
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
- Rule creation with valid data
|
||||||
|
- Rule creation with invalid data
|
||||||
|
- Rule update operations
|
||||||
|
- Rule deletion
|
||||||
|
- Cache operations
|
||||||
|
- Priority ordering
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
- End-to-end request flow
|
||||||
|
- Multiple rules interaction
|
||||||
|
- Cache hit/miss scenarios
|
||||||
|
- Whitelist default-deny behavior
|
||||||
|
- Performance under load
|
||||||
|
|
||||||
|
### Security Tests
|
||||||
|
- Invalid admin pubkey rejection
|
||||||
|
- Expired event rejection
|
||||||
|
- SQL injection attempts
|
||||||
|
- Cache poisoning attempts
|
||||||
|
- Priority bypass attempts
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
### Functional Requirements
|
||||||
|
- ✅ Rules can be created via Admin API
|
||||||
|
- ✅ Rules can be updated via Admin API
|
||||||
|
- ✅ Rules can be deleted via Admin API
|
||||||
|
- ✅ Rules are enforced during request validation
|
||||||
|
- ✅ Cache improves performance significantly
|
||||||
|
- ✅ Priority ordering works correctly
|
||||||
|
- ✅ Whitelist default-deny works correctly
|
||||||
|
|
||||||
|
### Performance Requirements
|
||||||
|
- ✅ Cache hit latency <200μs
|
||||||
|
- ✅ Full validation latency <3ms
|
||||||
|
- ✅ Cache hit rate >80% under load
|
||||||
|
- ✅ No memory leaks
|
||||||
|
- ✅ Database queries optimized
|
||||||
|
|
||||||
|
### Security Requirements
|
||||||
|
- ✅ Admin authentication required
|
||||||
|
- ✅ Input validation prevents injection
|
||||||
|
- ✅ Audit logging of all changes
|
||||||
|
- ✅ Cache keys prevent poisoning
|
||||||
|
- ✅ Whitelist bypass prevented
|
||||||
|
|
||||||
|
## Timeline Estimate
|
||||||
|
|
||||||
|
| Phase | Duration | Dependencies |
|
||||||
|
|-------|----------|--------------|
|
||||||
|
| Phase 1: Database Schema | 2-4 hours | None |
|
||||||
|
| Phase 2: Admin API | 6-8 hours | Phase 1 |
|
||||||
|
| Phase 3: Testing | 4-6 hours | Phase 2 |
|
||||||
|
| Phase 4: Documentation | 2-3 hours | Phase 3 |
|
||||||
|
| **Total** | **14-21 hours** | Sequential |
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Review this plan** with stakeholders
|
||||||
|
2. **Create Phase 1 migration script** in `db/migrations/`
|
||||||
|
3. **Test migration** on development database
|
||||||
|
4. **Implement Phase 2 endpoints** in `src/admin_api.c`
|
||||||
|
5. **Create test suite** in `tests/auth_rules_test.sh`
|
||||||
|
6. **Update documentation** in `docs/`
|
||||||
|
7. **Deploy to production** with migration guide
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The authentication rules system is **90% complete** - the core logic exists and is well-tested. This implementation plan focuses on the final 10%: adding database tables and Admin API endpoints. The work is straightforward, well-scoped, and can be completed in 2-3 days of focused development.
|
||||||
|
|
||||||
|
The system will provide powerful whitelist/blacklist functionality while maintaining the performance and security characteristics already present in the codebase.
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
// Version information (auto-updated by build system)
|
// Version information (auto-updated by build system)
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 1
|
#define VERSION_MINOR 1
|
||||||
#define VERSION_PATCH 6
|
#define VERSION_PATCH 7
|
||||||
#define VERSION "v0.1.6"
|
#define VERSION "v0.1.7"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|||||||
18
src/main.c
18
src/main.c
@@ -1509,6 +1509,20 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
|||||||
const char *message = result.reason[0] ? result.reason : "Authentication failed";
|
const char *message = result.reason[0] ? result.reason : "Authentication failed";
|
||||||
const char *details = "Authentication validation failed";
|
const char *details = "Authentication validation failed";
|
||||||
|
|
||||||
|
// Determine appropriate status code based on violation type
|
||||||
|
int status_code = 401; // Default: Unauthorized (no auth or invalid auth)
|
||||||
|
const char *violation_type = nostr_request_validator_get_last_violation_type();
|
||||||
|
|
||||||
|
// If auth rules denied the request, use 403 Forbidden instead of 401 Unauthorized
|
||||||
|
if (violation_type && (
|
||||||
|
strcmp(violation_type, "pubkey_blacklist") == 0 ||
|
||||||
|
strcmp(violation_type, "hash_blacklist") == 0 ||
|
||||||
|
strcmp(violation_type, "whitelist_violation") == 0 ||
|
||||||
|
strcmp(violation_type, "mime_blacklist") == 0 ||
|
||||||
|
strcmp(violation_type, "mime_whitelist_violation") == 0)) {
|
||||||
|
status_code = 403; // Forbidden: authenticated but not authorized
|
||||||
|
}
|
||||||
|
|
||||||
// Always include event JSON in details when auth header is provided for debugging
|
// Always include event JSON in details when auth header is provided for debugging
|
||||||
if (auth_header) {
|
if (auth_header) {
|
||||||
// Parse event JSON from auth header to show in details for debugging
|
// Parse event JSON from auth header to show in details for debugging
|
||||||
@@ -1526,8 +1540,8 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send_error_response(401, "authentication_failed", message, details);
|
send_error_response(status_code, "authentication_failed", message, details);
|
||||||
log_request(request_method, request_uri, "auth_failed", 401);
|
log_request(request_method, request_uri, "auth_failed", status_code);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -810,8 +810,17 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
|
|||||||
"checking database rules\n");
|
"checking database rules\n");
|
||||||
|
|
||||||
// Check database rules for authorization
|
// Check database rules for authorization
|
||||||
|
// For Blossom uploads, use hash from event 'x' tag instead of URI
|
||||||
|
const char *hash_for_rules = request->resource_hash;
|
||||||
|
if (event_kind == 24242 && strlen(expected_hash_from_event) == 64) {
|
||||||
|
hash_for_rules = expected_hash_from_event;
|
||||||
|
char hash_msg[256];
|
||||||
|
sprintf(hash_msg, "VALIDATOR_DEBUG: Using hash from Blossom event for rules: %.16s...\n", hash_for_rules);
|
||||||
|
validator_debug_log(hash_msg);
|
||||||
|
}
|
||||||
|
|
||||||
int rules_result = check_database_auth_rules(
|
int rules_result = check_database_auth_rules(
|
||||||
extracted_pubkey, request->operation, request->resource_hash);
|
extracted_pubkey, request->operation, hash_for_rules);
|
||||||
if (rules_result != NOSTR_SUCCESS) {
|
if (rules_result != NOSTR_SUCCESS) {
|
||||||
validator_debug_log(
|
validator_debug_log(
|
||||||
"VALIDATOR_DEBUG: STEP 14 FAILED - Database rules denied request\n");
|
"VALIDATOR_DEBUG: STEP 14 FAILED - Database rules denied request\n");
|
||||||
@@ -1334,9 +1343,10 @@ static int check_database_auth_rules(const char *pubkey, const char *operation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Check pubkey blacklist (highest priority)
|
// Step 1: Check pubkey blacklist (highest priority)
|
||||||
|
// Match both exact operation and wildcard '*'
|
||||||
const char *blacklist_sql =
|
const char *blacklist_sql =
|
||||||
"SELECT rule_type, description FROM auth_rules WHERE rule_type = "
|
"SELECT rule_type, description FROM auth_rules WHERE rule_type = "
|
||||||
"'pubkey_blacklist' AND rule_target = ? AND operation = ? AND enabled = "
|
"'pubkey_blacklist' AND rule_target = ? AND (operation = ? OR operation = '*') AND enabled = "
|
||||||
"1 ORDER BY priority LIMIT 1";
|
"1 ORDER BY priority LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, blacklist_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, blacklist_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
@@ -1369,9 +1379,10 @@ static int check_database_auth_rules(const char *pubkey, const char *operation,
|
|||||||
|
|
||||||
// Step 2: Check hash blacklist
|
// Step 2: Check hash blacklist
|
||||||
if (resource_hash) {
|
if (resource_hash) {
|
||||||
|
// Match both exact operation and wildcard '*'
|
||||||
const char *hash_blacklist_sql =
|
const char *hash_blacklist_sql =
|
||||||
"SELECT rule_type, description FROM auth_rules WHERE rule_type = "
|
"SELECT rule_type, description FROM auth_rules WHERE rule_type = "
|
||||||
"'hash_blacklist' AND rule_target = ? AND operation = ? AND enabled = "
|
"'hash_blacklist' AND rule_target = ? AND (operation = ? OR operation = '*') AND enabled = "
|
||||||
"1 ORDER BY priority LIMIT 1";
|
"1 ORDER BY priority LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, hash_blacklist_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, hash_blacklist_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
@@ -1408,9 +1419,10 @@ static int check_database_auth_rules(const char *pubkey, const char *operation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Check pubkey whitelist
|
// Step 3: Check pubkey whitelist
|
||||||
|
// Match both exact operation and wildcard '*'
|
||||||
const char *whitelist_sql =
|
const char *whitelist_sql =
|
||||||
"SELECT rule_type, description FROM auth_rules WHERE rule_type = "
|
"SELECT rule_type, description FROM auth_rules WHERE rule_type = "
|
||||||
"'pubkey_whitelist' AND rule_target = ? AND operation = ? AND enabled = "
|
"'pubkey_whitelist' AND rule_target = ? AND (operation = ? OR operation = '*') AND enabled = "
|
||||||
"1 ORDER BY priority LIMIT 1";
|
"1 ORDER BY priority LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, whitelist_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, whitelist_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
@@ -1436,9 +1448,10 @@ static int check_database_auth_rules(const char *pubkey, const char *operation,
|
|||||||
"not whitelisted\n");
|
"not whitelisted\n");
|
||||||
|
|
||||||
// Step 4: Check if any whitelist rules exist - if yes, deny by default
|
// Step 4: Check if any whitelist rules exist - if yes, deny by default
|
||||||
|
// Match both exact operation and wildcard '*'
|
||||||
const char *whitelist_exists_sql =
|
const char *whitelist_exists_sql =
|
||||||
"SELECT COUNT(*) FROM auth_rules WHERE rule_type = 'pubkey_whitelist' "
|
"SELECT COUNT(*) FROM auth_rules WHERE rule_type = 'pubkey_whitelist' "
|
||||||
"AND operation = ? AND enabled = 1 LIMIT 1";
|
"AND (operation = ? OR operation = '*') AND enabled = 1 LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, whitelist_exists_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, whitelist_exists_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
sqlite3_bind_text(stmt, 1, operation ? operation : "", -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, operation ? operation : "", -1, SQLITE_STATIC);
|
||||||
|
|||||||
1
tests/auth_test_tmp/blacklist_test1.txt
Normal file
1
tests/auth_test_tmp/blacklist_test1.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Content from blacklisted user
|
||||||
1
tests/auth_test_tmp/blacklist_test2.txt
Normal file
1
tests/auth_test_tmp/blacklist_test2.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Content from allowed user
|
||||||
1
tests/auth_test_tmp/cache_test1.txt
Normal file
1
tests/auth_test_tmp/cache_test1.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
First request - cache miss
|
||||||
1
tests/auth_test_tmp/cache_test2.txt
Normal file
1
tests/auth_test_tmp/cache_test2.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Second request - cache hit
|
||||||
1
tests/auth_test_tmp/cleanup_test.txt
Normal file
1
tests/auth_test_tmp/cleanup_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Testing after cleanup
|
||||||
1
tests/auth_test_tmp/disabled_rule_test.txt
Normal file
1
tests/auth_test_tmp/disabled_rule_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Testing disabled rule
|
||||||
1
tests/auth_test_tmp/enabled_rule_test.txt
Normal file
1
tests/auth_test_tmp/enabled_rule_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Testing enabled rule
|
||||||
1
tests/auth_test_tmp/hash_blacklist_test.txt
Normal file
1
tests/auth_test_tmp/hash_blacklist_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This specific file is blacklisted
|
||||||
1
tests/auth_test_tmp/hash_blacklist_test2.txt
Normal file
1
tests/auth_test_tmp/hash_blacklist_test2.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This file is allowed
|
||||||
1
tests/auth_test_tmp/mime_test1.txt
Normal file
1
tests/auth_test_tmp/mime_test1.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Plain text file
|
||||||
1
tests/auth_test_tmp/mime_whitelist_test.txt
Normal file
1
tests/auth_test_tmp/mime_whitelist_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Text file with whitelist active
|
||||||
1
tests/auth_test_tmp/operation_test.txt
Normal file
1
tests/auth_test_tmp/operation_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Testing operation-specific rules
|
||||||
1
tests/auth_test_tmp/priority_test.txt
Normal file
1
tests/auth_test_tmp/priority_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Testing priority ordering
|
||||||
1
tests/auth_test_tmp/test.txt
Normal file
1
tests/auth_test_tmp/test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
test content
|
||||||
1
tests/auth_test_tmp/whitelist_test1.txt
Normal file
1
tests/auth_test_tmp/whitelist_test1.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Content from whitelisted user
|
||||||
1
tests/auth_test_tmp/whitelist_test2.txt
Normal file
1
tests/auth_test_tmp/whitelist_test2.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Content from non-whitelisted user
|
||||||
1
tests/auth_test_tmp/wildcard_test.txt
Normal file
1
tests/auth_test_tmp/wildcard_test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Testing wildcard operation
|
||||||
392
tests/white_black_list_test.sh
Executable file
392
tests/white_black_list_test.sh
Executable file
@@ -0,0 +1,392 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# white_black_list_test.sh - Whitelist/Blacklist Rules Test Suite
|
||||||
|
# Tests the auth_rules table functionality for pubkey and MIME type filtering
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
SERVER_URL="http://localhost:9001"
|
||||||
|
UPLOAD_ENDPOINT="${SERVER_URL}/upload"
|
||||||
|
DB_PATH="db/ginxsom.db"
|
||||||
|
TEST_DIR="tests/auth_test_tmp"
|
||||||
|
|
||||||
|
# Test results tracking
|
||||||
|
TESTS_PASSED=0
|
||||||
|
TESTS_FAILED=0
|
||||||
|
TOTAL_TESTS=0
|
||||||
|
|
||||||
|
# Test keys for different scenarios
|
||||||
|
# Generated using: nak key public <privkey>
|
||||||
|
TEST_USER1_PRIVKEY="5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a"
|
||||||
|
TEST_USER1_PUBKEY="87d3561f19b74adbe8bf840682992466068830a9d8c36b4a0c99d36f826cb6cb"
|
||||||
|
|
||||||
|
TEST_USER2_PRIVKEY="182c3a5e3b7a1b7e4f5c6b7c8b4a5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
|
||||||
|
TEST_USER2_PUBKEY="0396b426090284a28294078dce53fe73791ab623c3fc46ab4409fea05109a6db"
|
||||||
|
|
||||||
|
TEST_USER3_PRIVKEY="abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234"
|
||||||
|
TEST_USER3_PUBKEY="769a740386211c76f81bb235de50a5e6fa463cb4fae25e62625607fc2cfc0f28"
|
||||||
|
|
||||||
|
# Helper function to record test results
|
||||||
|
record_test_result() {
|
||||||
|
local test_name="$1"
|
||||||
|
local expected="$2"
|
||||||
|
local actual="$3"
|
||||||
|
|
||||||
|
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||||
|
|
||||||
|
if [[ "$actual" == "$expected" ]]; then
|
||||||
|
echo "✅ $test_name - PASSED"
|
||||||
|
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||||
|
else
|
||||||
|
echo "❌ $test_name - FAILED (Expected: $expected, Got: $actual)"
|
||||||
|
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
for cmd in nak curl jq sqlite3; do
|
||||||
|
if ! command -v $cmd &> /dev/null; then
|
||||||
|
echo "❌ $cmd command not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if server is running
|
||||||
|
if ! curl -s -f "${SERVER_URL}/" > /dev/null 2>&1; then
|
||||||
|
echo "❌ Server not running at $SERVER_URL"
|
||||||
|
echo "Start with: ./restart-all.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if database exists
|
||||||
|
if [[ ! -f "$DB_PATH" ]]; then
|
||||||
|
echo "❌ Database not found at $DB_PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup test environment
|
||||||
|
mkdir -p "$TEST_DIR"
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " WHITELIST/BLACKLIST RULES TEST SUITE"
|
||||||
|
echo "=========================================="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
create_test_file() {
|
||||||
|
local filename="$1"
|
||||||
|
local content="${2:-test content for $filename}"
|
||||||
|
local filepath="$TEST_DIR/$filename"
|
||||||
|
echo "$content" > "$filepath"
|
||||||
|
echo "$filepath"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_auth_event() {
|
||||||
|
local privkey="$1"
|
||||||
|
local operation="$2"
|
||||||
|
local hash="$3"
|
||||||
|
local expiration_offset="${4:-3600}" # 1 hour default
|
||||||
|
|
||||||
|
local expiration=$(date -d "+${expiration_offset} seconds" +%s)
|
||||||
|
|
||||||
|
local event_args=(-k 24242 -c "" --tag "t=$operation" --tag "expiration=$expiration" --sec "$privkey")
|
||||||
|
|
||||||
|
if [[ -n "$hash" ]]; then
|
||||||
|
event_args+=(--tag "x=$hash")
|
||||||
|
fi
|
||||||
|
|
||||||
|
nak event "${event_args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_upload() {
|
||||||
|
local test_name="$1"
|
||||||
|
local privkey="$2"
|
||||||
|
local file_path="$3"
|
||||||
|
local expected_status="${4:-200}"
|
||||||
|
|
||||||
|
local file_hash=$(sha256sum "$file_path" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
# Create auth event
|
||||||
|
local event=$(create_auth_event "$privkey" "upload" "$file_hash")
|
||||||
|
local auth_header="Nostr $(echo "$event" | base64 -w 0)"
|
||||||
|
|
||||||
|
# Make upload request
|
||||||
|
local response_file=$(mktemp)
|
||||||
|
local http_status=$(curl -s -w "%{http_code}" \
|
||||||
|
-H "Authorization: $auth_header" \
|
||||||
|
-H "Content-Type: text/plain" \
|
||||||
|
--data-binary "@$file_path" \
|
||||||
|
-X PUT "$UPLOAD_ENDPOINT" \
|
||||||
|
-o "$response_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Show response if test fails
|
||||||
|
if [[ "$http_status" != "$expected_status" ]]; then
|
||||||
|
echo " Response: $(cat "$response_file")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$response_file"
|
||||||
|
|
||||||
|
# Record result
|
||||||
|
record_test_result "$test_name" "$expected_status" "$http_status"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up any existing rules from previous tests
|
||||||
|
echo "Cleaning up existing auth rules..."
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;" 2>/dev/null
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;" 2>/dev/null
|
||||||
|
|
||||||
|
# Enable authentication rules
|
||||||
|
echo "Enabling authentication rules..."
|
||||||
|
sqlite3 "$DB_PATH" "UPDATE config SET value = 'true' WHERE key = 'auth_rules_enabled';"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 1: PUBKEY BLACKLIST TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Test 1: Add pubkey blacklist rule
|
||||||
|
echo "Adding blacklist rule for TEST_USER3..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_blacklist', '$TEST_USER3_PUBKEY', 'upload', 10, 'Test blacklist');"
|
||||||
|
|
||||||
|
# Test 1a: Blacklisted user should be denied
|
||||||
|
test_file1=$(create_test_file "blacklist_test1.txt" "Content from blacklisted user")
|
||||||
|
test_upload "Test 1a: Blacklisted Pubkey Upload" "$TEST_USER3_PRIVKEY" "$test_file1" "403"
|
||||||
|
|
||||||
|
# Test 1b: Non-blacklisted user should succeed
|
||||||
|
test_file2=$(create_test_file "blacklist_test2.txt" "Content from allowed user")
|
||||||
|
test_upload "Test 1b: Non-Blacklisted Pubkey Upload" "$TEST_USER1_PRIVKEY" "$test_file2" "200"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 2: PUBKEY WHITELIST TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 2: Add pubkey whitelist rule
|
||||||
|
echo "Adding whitelist rule for TEST_USER1..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_whitelist', '$TEST_USER1_PUBKEY', 'upload', 300, 'Test whitelist');"
|
||||||
|
|
||||||
|
# Test 2a: Whitelisted user should succeed
|
||||||
|
test_file3=$(create_test_file "whitelist_test1.txt" "Content from whitelisted user")
|
||||||
|
test_upload "Test 2a: Whitelisted Pubkey Upload" "$TEST_USER1_PRIVKEY" "$test_file3" "200"
|
||||||
|
|
||||||
|
# Test 2b: Non-whitelisted user should be denied (whitelist default-deny)
|
||||||
|
test_file4=$(create_test_file "whitelist_test2.txt" "Content from non-whitelisted user")
|
||||||
|
test_upload "Test 2b: Non-Whitelisted Pubkey Upload" "$TEST_USER2_PRIVKEY" "$test_file4" "403"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 3: HASH BLACKLIST TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 3: Create a file and blacklist its hash
|
||||||
|
test_file5=$(create_test_file "hash_blacklist_test.txt" "This specific file is blacklisted")
|
||||||
|
BLACKLISTED_HASH=$(sha256sum "$test_file5" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
echo "Adding hash blacklist rule for $BLACKLISTED_HASH..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('hash_blacklist', '$BLACKLISTED_HASH', 'upload', 100, 'Test hash blacklist');"
|
||||||
|
|
||||||
|
# Test 3a: Blacklisted hash should be denied
|
||||||
|
test_upload "Test 3a: Blacklisted Hash Upload" "$TEST_USER1_PRIVKEY" "$test_file5" "403"
|
||||||
|
|
||||||
|
# Test 3b: Different file should succeed
|
||||||
|
test_file6=$(create_test_file "hash_blacklist_test2.txt" "This file is allowed")
|
||||||
|
test_upload "Test 3b: Non-Blacklisted Hash Upload" "$TEST_USER1_PRIVKEY" "$test_file6" "200"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 4: MIME TYPE BLACKLIST TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 4: Blacklist executable MIME types
|
||||||
|
echo "Adding MIME type blacklist rules..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('mime_blacklist', 'application/x-executable', 'upload', 200, 'Block executables');"
|
||||||
|
|
||||||
|
# Note: This test would require the server to detect MIME types from file content
|
||||||
|
# For now, we'll test with text/plain which should be allowed
|
||||||
|
test_file7=$(create_test_file "mime_test1.txt" "Plain text file")
|
||||||
|
test_upload "Test 4a: Allowed MIME Type Upload" "$TEST_USER1_PRIVKEY" "$test_file7" "200"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 5: MIME TYPE WHITELIST TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 5: Whitelist only image MIME types
|
||||||
|
echo "Adding MIME type whitelist rules..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('mime_whitelist', 'image/jpeg', 'upload', 400, 'Allow JPEG');"
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('mime_whitelist', 'image/png', 'upload', 400, 'Allow PNG');"
|
||||||
|
|
||||||
|
# Note: MIME type detection would need to be implemented in the server
|
||||||
|
# For now, text/plain should be denied if whitelist exists
|
||||||
|
test_file8=$(create_test_file "mime_whitelist_test.txt" "Text file with whitelist active")
|
||||||
|
test_upload "Test 5a: Non-Whitelisted MIME Type Upload" "$TEST_USER1_PRIVKEY" "$test_file8" "403"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 6: PRIORITY ORDERING TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 6: Blacklist should override whitelist (priority ordering)
|
||||||
|
echo "Adding both blacklist (priority 10) and whitelist (priority 300) for same pubkey..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_blacklist', '$TEST_USER1_PUBKEY', 'upload', 10, 'Blacklist priority test');"
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_whitelist', '$TEST_USER1_PUBKEY', 'upload', 300, 'Whitelist priority test');"
|
||||||
|
|
||||||
|
# Test 6a: Blacklist should win (lower priority number = higher priority)
|
||||||
|
test_file9=$(create_test_file "priority_test.txt" "Testing priority ordering")
|
||||||
|
test_upload "Test 6a: Blacklist Priority Over Whitelist" "$TEST_USER1_PRIVKEY" "$test_file9" "403"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 7: OPERATION-SPECIFIC RULES ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 7: Blacklist only for upload operation
|
||||||
|
echo "Adding blacklist rule for upload operation only..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_blacklist', '$TEST_USER2_PUBKEY', 'upload', 10, 'Upload-only blacklist');"
|
||||||
|
|
||||||
|
# Test 7a: Upload should be denied
|
||||||
|
test_file10=$(create_test_file "operation_test.txt" "Testing operation-specific rules")
|
||||||
|
test_upload "Test 7a: Operation-Specific Blacklist" "$TEST_USER2_PRIVKEY" "$test_file10" "403"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 8: WILDCARD OPERATION TESTS ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 8: Blacklist for all operations using wildcard
|
||||||
|
echo "Adding blacklist rule for all operations (*)..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_blacklist', '$TEST_USER3_PUBKEY', '*', 10, 'All operations blacklist');"
|
||||||
|
|
||||||
|
# Test 8a: Upload should be denied
|
||||||
|
test_file11=$(create_test_file "wildcard_test.txt" "Testing wildcard operation")
|
||||||
|
test_upload "Test 8a: Wildcard Operation Blacklist" "$TEST_USER3_PRIVKEY" "$test_file11" "403"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 9: ENABLED/DISABLED RULES ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 9: Disabled rule should not be enforced
|
||||||
|
echo "Adding disabled blacklist rule..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, enabled, description) VALUES ('pubkey_blacklist', '$TEST_USER1_PUBKEY', 'upload', 10, 0, 'Disabled blacklist');"
|
||||||
|
|
||||||
|
# Test 9a: Upload should succeed (rule is disabled)
|
||||||
|
test_file12=$(create_test_file "disabled_rule_test.txt" "Testing disabled rule")
|
||||||
|
test_upload "Test 9a: Disabled Rule Not Enforced" "$TEST_USER1_PRIVKEY" "$test_file12" "200"
|
||||||
|
|
||||||
|
# Test 9b: Enable the rule
|
||||||
|
echo "Enabling the blacklist rule..."
|
||||||
|
sqlite3 "$DB_PATH" "UPDATE auth_rules SET enabled = 1 WHERE rule_target = '$TEST_USER1_PUBKEY';"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;" # Clear cache
|
||||||
|
|
||||||
|
# Test 9c: Upload should now be denied
|
||||||
|
test_file13=$(create_test_file "enabled_rule_test.txt" "Testing enabled rule")
|
||||||
|
test_upload "Test 9c: Enabled Rule Enforced" "$TEST_USER1_PRIVKEY" "$test_file13" "403"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 10: CACHE FUNCTIONALITY ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean rules
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Test 10: Add a blacklist rule and verify cache is populated
|
||||||
|
echo "Adding blacklist rule to test caching..."
|
||||||
|
sqlite3 "$DB_PATH" "INSERT INTO auth_rules (rule_type, rule_target, operation, priority, description) VALUES ('pubkey_blacklist', '$TEST_USER2_PUBKEY', 'upload', 10, 'Cache test');"
|
||||||
|
|
||||||
|
# Test 10a: First request (cache miss)
|
||||||
|
test_file14=$(create_test_file "cache_test1.txt" "First request - cache miss")
|
||||||
|
test_upload "Test 10a: First Request (Cache Miss)" "$TEST_USER2_PRIVKEY" "$test_file14" "403"
|
||||||
|
|
||||||
|
# Test 10b: Second request (should hit cache)
|
||||||
|
test_file15=$(create_test_file "cache_test2.txt" "Second request - cache hit")
|
||||||
|
test_upload "Test 10b: Second Request (Cache Hit)" "$TEST_USER2_PRIVKEY" "$test_file15" "403"
|
||||||
|
|
||||||
|
# Test 10c: Verify cache entry exists
|
||||||
|
CACHE_COUNT=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM auth_rules_cache WHERE pubkey = '$TEST_USER2_PUBKEY';" 2>/dev/null)
|
||||||
|
if [[ "$CACHE_COUNT" -gt 0 ]]; then
|
||||||
|
record_test_result "Test 10c: Cache Entry Created" "1" "1"
|
||||||
|
else
|
||||||
|
record_test_result "Test 10c: Cache Entry Created" "1" "0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== SECTION 11: CLEANUP AND RESET ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Clean up all test rules
|
||||||
|
echo "Cleaning up test rules..."
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules;"
|
||||||
|
sqlite3 "$DB_PATH" "DELETE FROM auth_rules_cache;"
|
||||||
|
|
||||||
|
# Verify cleanup
|
||||||
|
RULE_COUNT=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM auth_rules;" 2>/dev/null)
|
||||||
|
if [[ "$RULE_COUNT" -eq 0 ]]; then
|
||||||
|
record_test_result "Test 11a: Rules Cleanup" "0" "0"
|
||||||
|
else
|
||||||
|
record_test_result "Test 11a: Rules Cleanup" "0" "$RULE_COUNT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CACHE_COUNT=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM auth_rules_cache;" 2>/dev/null)
|
||||||
|
if [[ "$CACHE_COUNT" -eq 0 ]]; then
|
||||||
|
record_test_result "Test 11b: Cache Cleanup" "0" "0"
|
||||||
|
else
|
||||||
|
record_test_result "Test 11b: Cache Cleanup" "0" "$CACHE_COUNT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test that uploads work again after cleanup
|
||||||
|
test_file16=$(create_test_file "cleanup_test.txt" "Testing after cleanup")
|
||||||
|
test_upload "Test 11c: Upload After Cleanup" "$TEST_USER1_PRIVKEY" "$test_file16" "200"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=========================================="
|
||||||
|
echo " TEST SUITE RESULTS"
|
||||||
|
echo "=========================================="
|
||||||
|
echo
|
||||||
|
echo "Total Tests: $TOTAL_TESTS"
|
||||||
|
echo "✅ Passed: $TESTS_PASSED"
|
||||||
|
echo "❌ Failed: $TESTS_FAILED"
|
||||||
|
echo
|
||||||
|
if [[ $TESTS_FAILED -eq 0 ]]; then
|
||||||
|
echo "🎉 ALL TESTS PASSED!"
|
||||||
|
echo
|
||||||
|
echo "Whitelist/Blacklist functionality verified:"
|
||||||
|
echo "- Pubkey blacklist: Working"
|
||||||
|
echo "- Pubkey whitelist: Working"
|
||||||
|
echo "- Hash blacklist: Working"
|
||||||
|
echo "- MIME type rules: Working"
|
||||||
|
echo "- Priority ordering: Working"
|
||||||
|
echo "- Operation-specific rules: Working"
|
||||||
|
echo "- Wildcard operations: Working"
|
||||||
|
echo "- Enable/disable rules: Working"
|
||||||
|
echo "- Cache functionality: Working"
|
||||||
|
else
|
||||||
|
echo "⚠️ Some tests failed. Check output above for details."
|
||||||
|
echo "Success rate: $(( (TESTS_PASSED * 100) / TOTAL_TESTS ))%"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
echo "To clean up test data: rm -rf $TEST_DIR"
|
||||||
|
echo "=========================================="
|
||||||
Reference in New Issue
Block a user