Working on API
This commit is contained in:
513
IMPLEMENT_API.md
Normal file
513
IMPLEMENT_API.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# Implementation Plan: Enhanced Admin Event API Structure
|
||||
|
||||
## Current Issue
|
||||
|
||||
The current admin event routing at [`main.c:3248-3268`](src/main.c:3248) has a security vulnerability:
|
||||
|
||||
```c
|
||||
if (event_kind == 23455 || event_kind == 23456) {
|
||||
// Admin event processing
|
||||
int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi);
|
||||
} else {
|
||||
// Regular event storage and broadcasting
|
||||
}
|
||||
```
|
||||
|
||||
**Problem**: Any event with these kinds gets routed to admin processing, regardless of authorization. This allows unauthorized users to send admin events that could be processed as legitimate admin commands.
|
||||
|
||||
**Note**: Event kinds 33334 and 33335 are no longer used and have been removed from the admin event routing.
|
||||
|
||||
## Required Security Enhancement
|
||||
|
||||
Admin events must be validated for proper authorization BEFORE routing to admin processing:
|
||||
|
||||
1. **Relay Public Key Check**: Event must have a `p` tag equal to the relay's public key
|
||||
2. **Admin Signature Check**: Event must be signed by an authorized admin private key
|
||||
3. **Fallback to Regular Processing**: If authorization fails, treat as regular event (not admin event)
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Add Admin Authorization Validation
|
||||
|
||||
#### 1.1 Create Consolidated Admin Authorization Function
|
||||
**Location**: [`src/main.c`](src/main.c) or [`src/config.c`](src/config.c)
|
||||
|
||||
```c
|
||||
/**
|
||||
* Consolidated admin event authorization validator
|
||||
* Implements defense-in-depth security for admin events
|
||||
*
|
||||
* @param event - The event to validate for admin authorization
|
||||
* @param error_message - Buffer for detailed error messages
|
||||
* @param error_size - Size of error message buffer
|
||||
* @return 0 if authorized, -1 if unauthorized, -2 if validation error
|
||||
*/
|
||||
int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size) {
|
||||
if (!event) {
|
||||
snprintf(error_message, error_size, "admin_auth: null event");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Extract event components
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
|
||||
cJSON* tags_obj = cJSON_GetObjectItem(event, "tags");
|
||||
|
||||
if (!kind_obj || !pubkey_obj || !tags_obj) {
|
||||
snprintf(error_message, error_size, "admin_auth: missing required fields");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Validation Layer 1: Kind Check
|
||||
int event_kind = (int)cJSON_GetNumberValue(kind_obj);
|
||||
if (event_kind != 23455 && event_kind != 23456) {
|
||||
snprintf(error_message, error_size, "admin_auth: not an admin event kind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validation Layer 2: Relay Targeting Check
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
if (!relay_pubkey) {
|
||||
snprintf(error_message, error_size, "admin_auth: relay pubkey not configured");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Check for 'p' tag targeting this relay
|
||||
int has_relay_target = 0;
|
||||
if (cJSON_IsArray(tags_obj)) {
|
||||
cJSON* tag = NULL;
|
||||
cJSON_ArrayForEach(tag, tags_obj) {
|
||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
||||
cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
|
||||
cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
|
||||
|
||||
if (cJSON_IsString(tag_name) && cJSON_IsString(tag_value)) {
|
||||
const char* name = cJSON_GetStringValue(tag_name);
|
||||
const char* value = cJSON_GetStringValue(tag_value);
|
||||
|
||||
if (strcmp(name, "p") == 0 && strcmp(value, relay_pubkey) == 0) {
|
||||
has_relay_target = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_relay_target) {
|
||||
// Admin event for different relay - not unauthorized, just not for us
|
||||
snprintf(error_message, error_size, "admin_auth: admin event for different relay");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validation Layer 3: Admin Signature Check (only if targeting this relay)
|
||||
const char* event_pubkey = cJSON_GetStringValue(pubkey_obj);
|
||||
if (!event_pubkey) {
|
||||
snprintf(error_message, error_size, "admin_auth: invalid pubkey format");
|
||||
return -2;
|
||||
}
|
||||
|
||||
const char* admin_pubkey = get_config_value("admin_pubkey");
|
||||
if (!admin_pubkey || strcmp(event_pubkey, admin_pubkey) != 0) {
|
||||
// This is the ONLY case where we log as "Unauthorized admin event attempt"
|
||||
// because it's targeting THIS relay but from wrong admin
|
||||
snprintf(error_message, error_size, "admin_auth: unauthorized admin for this relay");
|
||||
log_warning("SECURITY: Unauthorized admin event attempt for this relay");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// All validation layers passed
|
||||
log_info("ADMIN: Admin event authorized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### 1.2 Update Event Routing Logic
|
||||
**Location**: [`main.c:3248`](src/main.c:3248)
|
||||
|
||||
```c
|
||||
// Current problematic code:
|
||||
if (event_kind == 23455 || event_kind == 23456) {
|
||||
// Admin event processing
|
||||
int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi);
|
||||
} else {
|
||||
// Regular event storage and broadcasting
|
||||
}
|
||||
|
||||
// Enhanced secure code with consolidated authorization:
|
||||
if (result == 0) {
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
if (kind_obj && cJSON_IsNumber(kind_obj)) {
|
||||
int event_kind = (int)cJSON_GetNumberValue(kind_obj);
|
||||
|
||||
// Check if this is an admin event
|
||||
if (event_kind == 23455 || event_kind == 23456) {
|
||||
// Use consolidated authorization check
|
||||
char auth_error[512] = {0};
|
||||
int auth_result = is_authorized_admin_event(event, auth_error, sizeof(auth_error));
|
||||
|
||||
if (auth_result == 0) {
|
||||
// Authorized admin event - process through admin API
|
||||
char admin_error[512] = {0};
|
||||
int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi);
|
||||
|
||||
if (admin_result != 0) {
|
||||
result = -1;
|
||||
strncpy(error_message, admin_error, sizeof(error_message) - 1);
|
||||
}
|
||||
// Admin events are NOT broadcast to subscriptions
|
||||
} else {
|
||||
// Unauthorized admin event - treat as regular event
|
||||
log_warning("Unauthorized admin event treated as regular event");
|
||||
if (store_event(event) != 0) {
|
||||
result = -1;
|
||||
strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1);
|
||||
} else {
|
||||
broadcast_event_to_subscriptions(event);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Regular event - normal processing
|
||||
if (store_event(event) != 0) {
|
||||
result = -1;
|
||||
strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1);
|
||||
} else {
|
||||
broadcast_event_to_subscriptions(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Enhanced Admin Event Processing
|
||||
|
||||
#### 2.1 Admin Event Validation in Config System
|
||||
**Location**: [`src/config.c`](src/config.c) - [`process_admin_event_in_config()`](src/config.c:2065)
|
||||
|
||||
Add additional validation within the admin processing function:
|
||||
|
||||
```c
|
||||
int process_admin_event_in_config(cJSON* event, char* error_buffer, size_t error_buffer_size, struct lws* wsi) {
|
||||
// Double-check authorization (defense in depth)
|
||||
if (!is_authorized_admin_event(event)) {
|
||||
snprintf(error_buffer, error_buffer_size, "unauthorized: not a valid admin event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Continue with existing admin event processing...
|
||||
// ... rest of function unchanged
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 Logging and Monitoring
|
||||
Add comprehensive logging for admin event attempts:
|
||||
|
||||
```c
|
||||
// In the routing logic - enhanced logging
|
||||
cJSON* kind_obj = cJSON_GetObjectItem(event, "kind");
|
||||
cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
|
||||
int event_kind = kind_obj ? cJSON_GetNumberValue(kind_obj) : -1;
|
||||
const char* event_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : "unknown";
|
||||
|
||||
if (is_authorized_admin_event(event)) {
|
||||
char log_msg[256];
|
||||
snprintf(log_msg, sizeof(log_msg),
|
||||
"ADMIN EVENT: Authorized admin event (kind=%d) from pubkey=%.16s...",
|
||||
event_kind, event_pubkey);
|
||||
log_info(log_msg);
|
||||
} else if (event_kind == 23455 || event_kind == 23456) {
|
||||
// This catches unauthorized admin event attempts
|
||||
char log_msg[256];
|
||||
snprintf(log_msg, sizeof(log_msg),
|
||||
"SECURITY: Unauthorized admin event attempt (kind=%d) from pubkey=%.16s...",
|
||||
event_kind, event_pubkey);
|
||||
log_warning(log_msg);
|
||||
}
|
||||
```
|
||||
|
||||
## Phase 3: Unified Output Flow Architecture
|
||||
|
||||
### 3.1 Current Output Flow Analysis
|
||||
|
||||
After analyzing both [`main.c`](src/main.c) and [`config.c`](src/config.c), the **admin event responses already flow through the standard WebSocket output pipeline**. This is the correct architecture and requires no changes.
|
||||
|
||||
#### Standard WebSocket Output Pipeline
|
||||
|
||||
**Regular Events** ([`main.c:2978-2996`](src/main.c:2978)):
|
||||
```c
|
||||
// Database query responses
|
||||
unsigned char* buf = malloc(LWS_PRE + msg_len);
|
||||
memcpy(buf + LWS_PRE, msg_str, msg_len);
|
||||
lws_write(wsi, buf + LWS_PRE, msg_len, LWS_WRITE_TEXT);
|
||||
free(buf);
|
||||
```
|
||||
|
||||
**OK Responses** ([`main.c:3342-3375`](src/main.c:3342)):
|
||||
```c
|
||||
// Event processing results: ["OK", event_id, success_boolean, message]
|
||||
unsigned char *buf = malloc(LWS_PRE + response_len);
|
||||
memcpy(buf + LWS_PRE, response_str, response_len);
|
||||
lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT);
|
||||
free(buf);
|
||||
```
|
||||
|
||||
#### Admin Event Output Pipeline (Already Unified)
|
||||
|
||||
**Admin Responses** ([`config.c:2363-2414`](src/config.c:2363)):
|
||||
```c
|
||||
// Admin query responses use IDENTICAL pattern
|
||||
int send_websocket_response_data(struct lws* wsi, cJSON* response_data) {
|
||||
unsigned char* buf = malloc(LWS_PRE + response_len);
|
||||
memcpy(buf + LWS_PRE, response_str, response_len);
|
||||
|
||||
// Same lws_write() call as regular events
|
||||
int result = lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT);
|
||||
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Unified Output Flow Confirmation
|
||||
|
||||
✅ **Admin responses already use the same WebSocket transmission mechanism as regular events**
|
||||
|
||||
✅ **Both admin and regular events use identical buffer allocation patterns**
|
||||
|
||||
✅ **Both admin and regular events use the same [`lws_write()`](src/config.c:2393) function**
|
||||
|
||||
✅ **Both admin and regular events follow the same cleanup patterns**
|
||||
|
||||
### 3.3 Output Flow Integration Points
|
||||
|
||||
The admin event processing in [`config.c:2436`](src/config.c:2436) already integrates correctly with the unified output system:
|
||||
|
||||
1. **Admin Query Processing** ([`config.c:2568-2583`](src/config.c:2568)):
|
||||
- Auth queries return structured JSON via [`send_websocket_response_data()`](src/config.c:2571)
|
||||
- System commands return status data via [`send_websocket_response_data()`](src/config.c:2631)
|
||||
|
||||
2. **Response Format Consistency**:
|
||||
- Admin responses use standard JSON format
|
||||
- Regular events use standard Nostr event format
|
||||
- Both transmitted through same WebSocket pipeline
|
||||
|
||||
3. **Error Handling Consistency**:
|
||||
- Admin errors returned via same WebSocket connection
|
||||
- Regular event errors returned via OK messages
|
||||
- Both use identical transmission mechanism
|
||||
|
||||
### 3.4 Key Architectural Benefits
|
||||
|
||||
**No Changes Required**: The output flow is already unified and correctly implemented.
|
||||
|
||||
**Security Separation**: Admin events are processed separately but responses flow through the same secure WebSocket channel.
|
||||
|
||||
**Performance Consistency**: Both admin and regular responses use the same optimized transmission path.
|
||||
|
||||
**Maintenance Simplicity**: Single WebSocket output pipeline reduces complexity and potential bugs.
|
||||
|
||||
### 3.5 Admin Event Flow Summary
|
||||
|
||||
```
|
||||
Admin Event Input → Authorization Check → Admin Processing → Unified WebSocket Output
|
||||
Regular Event Input → Validation → Storage + Broadcast → Unified WebSocket Output
|
||||
```
|
||||
|
||||
Both flows converge at the **Unified WebSocket Output** stage, which is already correctly implemented.
|
||||
|
||||
## Phase 4: Integration Points for Secure Admin Event Routing
|
||||
|
||||
### 4.1 Configuration System Integration
|
||||
|
||||
**Required Configuration Values**:
|
||||
- `admin_pubkey` - Public key of authorized administrator
|
||||
- `relay_pubkey` - Public key of this relay instance
|
||||
|
||||
**Integration Points**:
|
||||
1. [`get_config_value()`](src/config.c) - Used by authorization function
|
||||
2. [`get_relay_pubkey_cached()`](src/config.c) - Used for relay targeting validation
|
||||
3. Configuration loading during startup - Must ensure admin/relay pubkeys are available
|
||||
|
||||
### 4.3 Forward Declarations Required
|
||||
|
||||
**Location**: [`src/main.c`](src/main.c) - Add near other forward declarations (around line 230)
|
||||
|
||||
```c
|
||||
// Forward declarations for enhanced admin event authorization
|
||||
int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size);
|
||||
```
|
||||
|
||||
### 4.4 Error Handling Integration
|
||||
|
||||
**Enhanced Error Response System**:
|
||||
|
||||
```c
|
||||
// In main.c event processing - enhanced error handling for admin events
|
||||
if (auth_result != 0) {
|
||||
// Admin authorization failed - send detailed OK response
|
||||
cJSON* event_id = cJSON_GetObjectItem(event, "id");
|
||||
if (event_id && cJSON_IsString(event_id)) {
|
||||
cJSON* response = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(response, cJSON_CreateString("OK"));
|
||||
cJSON_AddItemToArray(response, cJSON_CreateString(cJSON_GetStringValue(event_id)));
|
||||
cJSON_AddItemToArray(response, cJSON_CreateBool(0)); // Failed
|
||||
cJSON_AddItemToArray(response, cJSON_CreateString(auth_error));
|
||||
|
||||
// Send via standard WebSocket output pipeline
|
||||
char *response_str = cJSON_Print(response);
|
||||
if (response_str) {
|
||||
size_t response_len = strlen(response_str);
|
||||
unsigned char *buf = malloc(LWS_PRE + response_len);
|
||||
if (buf) {
|
||||
memcpy(buf + LWS_PRE, response_str, response_len);
|
||||
lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT);
|
||||
free(buf);
|
||||
}
|
||||
free(response_str);
|
||||
}
|
||||
cJSON_Delete(response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 Logging Integration Points
|
||||
|
||||
**Console Logging**: Uses existing [`log_warning()`](src/main.c:993), [`log_info()`](src/main.c:972) functions
|
||||
|
||||
**Security Event Categories**:
|
||||
- Admin authorization success logged via `log_info()`
|
||||
- Admin authorization failures logged via `log_warning()`
|
||||
- Admin event processing logged via existing admin logging
|
||||
|
||||
## Phase 5: Detailed Function Specifications
|
||||
|
||||
### 5.1 Core Authorization Function
|
||||
|
||||
**Function**: `is_authorized_admin_event()`
|
||||
**Location**: [`src/main.c`](src/main.c) or [`src/config.c`](src/config.c)
|
||||
**Dependencies**:
|
||||
- `get_config_value()` for admin/relay pubkeys
|
||||
- `log_warning()` and `log_info()` for logging
|
||||
- `cJSON` library for event parsing
|
||||
|
||||
**Return Values**:
|
||||
- `0` - Event is authorized for admin processing
|
||||
- `-1` - Event is unauthorized (treat as regular event)
|
||||
- `-2` - Validation error (malformed event)
|
||||
|
||||
**Error Handling**: Detailed error messages in provided buffer for client feedback
|
||||
|
||||
### 5.2 Enhanced Event Routing
|
||||
|
||||
**Location**: [`main.c:3248-3340`](src/main.c:3248)
|
||||
**Integration**: Replaces existing admin event routing logic
|
||||
**Dependencies**:
|
||||
- `is_authorized_admin_event()` for authorization
|
||||
- `process_admin_event_in_config()` for admin processing
|
||||
- `store_event()` and `broadcast_event_to_subscriptions()` for regular events
|
||||
|
||||
**Security Features**:
|
||||
- Graceful degradation for unauthorized admin events
|
||||
- Comprehensive logging of authorization attempts
|
||||
- No broadcast of admin events to subscriptions
|
||||
- Detailed error responses for failed authorization
|
||||
|
||||
### 5.4 Defense-in-Depth Validation
|
||||
|
||||
**Primary Validation**: In main event routing logic
|
||||
**Secondary Validation**: In `process_admin_event_in_config()` function
|
||||
**Tertiary Validation**: In individual admin command handlers
|
||||
|
||||
**Validation Layers**:
|
||||
1. **Kind Check** - Must be admin event kind (23455/23456)
|
||||
2. **Relay Targeting Check** - Must have 'p' tag with this relay's pubkey
|
||||
3. **Admin Signature Check** - Must be signed by authorized admin (only if targeting this relay)
|
||||
4. **Processing Check** - Additional validation in admin handlers
|
||||
|
||||
**Security Logic**:
|
||||
- If no 'p' tag for this relay → Admin event for different relay (not unauthorized)
|
||||
- If 'p' tag for this relay + wrong admin signature → "Unauthorized admin event attempt"
|
||||
|
||||
## Phase 6: Event Flow Documentation
|
||||
|
||||
### 6.1 Complete Event Processing Flow
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ WebSocket Input │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Unified │
|
||||
│ Validation │ ← nostr_validate_unified_request()
|
||||
└─────────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Kind-Based │
|
||||
│ Routing Check │ ← Check if kind 23455/23456
|
||||
└─────────┬───────┘
|
||||
│
|
||||
┌────▼────┐
|
||||
│ Admin? │
|
||||
└────┬────┘
|
||||
│
|
||||
┌─────▼─────┐ ┌─────────────┐
|
||||
│ YES │ │ NO │
|
||||
│ │ │ │
|
||||
▼ │ ▼ │
|
||||
┌─────────────┐ │ ┌─────────────┐ │
|
||||
│ Admin │ │ │ Regular │ │
|
||||
│ Authorization│ │ │ Event │ │
|
||||
│ Check │ │ │ Processing │ │
|
||||
└─────┬───────┘ │ └─────┬───────┘ │
|
||||
│ │ │ │
|
||||
┌────▼────┐ │ ▼ │
|
||||
│Authorized?│ │ ┌─────────────┐ │
|
||||
└────┬────┘ │ │ store_event()│ │
|
||||
│ │ │ + │ │
|
||||
┌─────▼─────┐ │ │ broadcast() │ │
|
||||
│ YES NO │ │ └─────┬───────┘ │
|
||||
│ │ │ │ │ │ │
|
||||
│ ▼ ▼ │ │ ▼ │
|
||||
│┌─────┐┌───┴┐ │ ┌─────────────┐ │
|
||||
││Admin││Treat│ │ │ WebSocket │ │
|
||||
││API ││as │ │ │ OK Response │ │
|
||||
││ ││Reg │ │ └─────────────┘ │
|
||||
│└──┬──┘└───┬┘ │ │
|
||||
│ │ │ │ │
|
||||
│ ▼ │ │ │
|
||||
│┌─────────┐│ │ │
|
||||
││WebSocket││ │ │
|
||||
││Response ││ │ │
|
||||
│└─────────┘│ │ │
|
||||
└───────────┴───┘ │
|
||||
│ │
|
||||
└───────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Unified │
|
||||
│ WebSocket │
|
||||
│ Output │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
### 6.2 Security Decision Points
|
||||
|
||||
1. **Event Kind Check** - Identifies potential admin events
|
||||
2. **Authorization Validation** - Three-layer security check
|
||||
3. **Routing Decision** - Admin API vs Regular processing
|
||||
4. **Response Generation** - Unified output pipeline
|
||||
5. **Audit Logging** - Security event tracking
|
||||
|
||||
### 6.3 Error Handling Paths
|
||||
|
||||
**Validation Errors**: Return detailed error messages via OK response
|
||||
**Authorization Failures**: Log security event + treat as regular event
|
||||
**Processing Errors**: Return admin-specific error responses
|
||||
**System Errors**: Fallback to standard error handling
|
||||
|
||||
This completes the comprehensive implementation plan for the enhanced admin event API structure with unified output flow architecture.
|
||||
Reference in New Issue
Block a user