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.
|
||||
371
README.md
371
README.md
@@ -24,55 +24,66 @@ Do NOT modify the formatting, add emojis, or change the text. Keep the simple fo
|
||||
|
||||
## 🔧 Administrator API
|
||||
|
||||
C-Relay uses an innovative **event-based administration system** where all configuration and management commands are sent as signed Nostr events using the admin private key generated during first startup. Admin commands use ephemeral kinds 23455 and 23456 with **optional NIP-44 encryption** for enhanced security.
|
||||
C-Relay uses an innovative **event-based administration system** where all configuration and management commands are sent as signed Nostr events using the admin private key generated during first startup. All admin commands use **tag-based parameters** for simplicity and compatibility.
|
||||
|
||||
### Authentication
|
||||
|
||||
All admin commands require signing with the admin private key displayed during first-time startup. **Save this key securely** - it cannot be recovered and is needed for all administrative operations.
|
||||
|
||||
### Security Options
|
||||
### Event Structure
|
||||
|
||||
**Standard Mode (Plaintext):** Commands sent in tags as normal
|
||||
**Encrypted Mode (NIP-44):** Commands encrypted in content field, no tags used
|
||||
All admin commands use the same unified event structure with tag-based parameters:
|
||||
|
||||
### Kind 23455: Configuration Management (Ephemeral)
|
||||
Update relay configuration parameters or query available settings.
|
||||
|
||||
**Configuration Update:**
|
||||
**Admin Command Event:**
|
||||
```json
|
||||
{
|
||||
"kind": 23455,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"id": "event_id",
|
||||
"pubkey": "admin_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23456,
|
||||
"content": "<nip44 encrypted command>",
|
||||
"tags": [
|
||||
["config_key1", "config_value1"],
|
||||
["config_key2", "config_value2"]
|
||||
]
|
||||
["p", "relay_public_key"],
|
||||
],
|
||||
"sig": "event_signature"
|
||||
}
|
||||
```
|
||||
|
||||
**Query Available Config Keys:**
|
||||
**Admin Response Event:**
|
||||
```json
|
||||
{
|
||||
"kind": 23455,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "<nip44 encrypted response>",
|
||||
"tags": [
|
||||
["config_query", "list_all_keys"]
|
||||
]
|
||||
}
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
|
||||
**Get Current Configuration:**
|
||||
```json
|
||||
{
|
||||
"kind": 23455,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["config_query", "get_current_config"]
|
||||
]
|
||||
}
|
||||
```
|
||||
### Admin Commands
|
||||
|
||||
**Available Configuration Keys:**
|
||||
All commands are sent as nip44 encrypted content. The following table lists all available commands:
|
||||
|
||||
| Command Type | Tag Format | Description |
|
||||
|--------------|------------|-------------|
|
||||
| **Configuration Management** |
|
||||
| `config_update` | `["relay_description", "My Relay"]` | Update relay configuration parameters |
|
||||
| `config_query` | `["config_query", "list_all_keys"]` | List all available configuration keys |
|
||||
| **Auth Rules Management** |
|
||||
| `auth_add_blacklist` | `["blacklist", "pubkey", "abc123..."]` | Add pubkey to blacklist |
|
||||
| `auth_add_whitelist` | `["whitelist", "pubkey", "def456..."]` | Add pubkey to whitelist |
|
||||
| `auth_query_all` | `["auth_query", "all"]` | Query all auth rules |
|
||||
| `auth_query_type` | `["auth_query", "whitelist"]` | Query specific rule type |
|
||||
| `auth_query_pattern` | `["auth_query", "pattern", "abc123..."]` | Query specific pattern |
|
||||
| **System Commands** |
|
||||
| `system_clear_auth` | `["system_command", "clear_all_auth_rules"]` | Clear all auth rules |
|
||||
| `system_status` | `["system_command", "system_status"]` | Get system status |
|
||||
|
||||
### Available Configuration Keys
|
||||
|
||||
**Basic Relay Settings:**
|
||||
- `relay_description`: Relay description text
|
||||
@@ -92,259 +103,69 @@ Update relay configuration parameters or query available settings.
|
||||
- `pow_min_difficulty`: Minimum proof-of-work difficulty
|
||||
- `nip40_expiration_enabled`: Enable event expiration (`true`/`false`)
|
||||
|
||||
### Kind 23456: Auth Rules & System Management (Ephemeral)
|
||||
Manage whitelist/blacklist rules and system administration commands.
|
||||
|
||||
**Add Blacklist Rule:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["blacklist", "pubkey", "deadbeef1234abcd..."]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Add Whitelist Rule:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["whitelist", "pubkey", "cafebabe5678efgh..."]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Remove Rule:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["blacklist", "pubkey", "deadbeef1234abcd..."]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Query Auth Rules:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["auth_query", "all"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Query Specific Rule Type:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["auth_query", "whitelist"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Query Specific Pattern:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["auth_query", "pattern", "deadbeef1234abcd..."]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Clear All Auth Rules:**
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"content": "<optional nip-44 encrypted tags>",
|
||||
"tags": [
|
||||
["system_command", "clear_all_auth_rules"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Response Format
|
||||
All admin commands return JSON responses via WebSocket:
|
||||
|
||||
All admin commands return **signed EVENT responses** via WebSocket following standard Nostr protocol. Responses use JSON content with structured data.
|
||||
|
||||
#### Response Examples
|
||||
|
||||
**Success Response:**
|
||||
```json
|
||||
["OK", "event_id", true, "Operation completed successfully"]
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"status\": \"success\", \"message\": \"Operation completed successfully\"}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
|
||||
**Error Response:**
|
||||
```json
|
||||
["OK", "event_id", false, "Error: invalid configuration value"]
|
||||
```
|
||||
|
||||
### Command Examples
|
||||
|
||||
**Using `nak` CLI tool:**
|
||||
|
||||
```bash
|
||||
# Set environment variables
|
||||
ADMIN_PRIVKEY="your_admin_private_key_here"
|
||||
RELAY_PUBKEY="your_relay_public_key_here"
|
||||
RELAY_URL="ws://localhost:8888"
|
||||
|
||||
# List all available configuration keys
|
||||
nak event -k 23455 --content "Discovery query" \
|
||||
-t "config_query=list_all_keys" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Enable whitelist/blacklist auth rules
|
||||
nak event -k 23455 --content "Enable auth rules" \
|
||||
-t "auth_enabled=true" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Add user to blacklist
|
||||
nak event -k 23456 --content "Block spam user" \
|
||||
-t "blacklist=pubkey;$SPAM_USER_PUBKEY" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Add user to whitelist
|
||||
nak event -k 23456 --content "Allow trusted user" \
|
||||
-t "whitelist=pubkey;$TRUSTED_USER_PUBKEY" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Query all current auth rules
|
||||
nak event -k 23456 --content "Get all auth rules" \
|
||||
-t "auth_query=all" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Query only whitelist rules
|
||||
nak event -k 23456 --content "Get whitelist rules" \
|
||||
-t "auth_query=whitelist" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Query only blacklist rules
|
||||
nak event -k 23456 --content "Get blacklist rules" \
|
||||
-t "auth_query=blacklist" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Check if specific pattern exists
|
||||
nak event -k 23456 --content "Check user status" \
|
||||
-t "auth_query=pattern;$CHECK_USER_PUBKEY" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Clear all auth rules (for testing)
|
||||
nak event -k 23456 --content "Clear all rules" \
|
||||
-t "system_command=clear_all_auth_rules" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Update relay description
|
||||
nak event -k 23455 --content "Update description" \
|
||||
-t "relay_description=My awesome relay" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Set connection limits
|
||||
nak event -k 23455 --content "Update limits" \
|
||||
-t "max_connections=500" \
|
||||
-t "max_subscriptions_per_client=10" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
```
|
||||
|
||||
**For encrypted commands (NIP-44), use empty tags and encrypted content:**
|
||||
```bash
|
||||
# Enable auth rules (encrypted)
|
||||
ENCRYPTED_TAGS=$(echo '[["auth_enabled","true"]]' | nip44_encrypt_with_relay_pubkey)
|
||||
nak event -k 23455 --content "{\"encrypted_tags\":\"$ENCRYPTED_TAGS\"}" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Add user to blacklist (encrypted)
|
||||
ENCRYPTED_TAGS=$(echo '[["blacklist","pubkey","'$SPAM_USER_PUBKEY'"]]' | nip44_encrypt_with_relay_pubkey)
|
||||
nak event -k 23456 --content "{\"encrypted_tags\":\"$ENCRYPTED_TAGS\"}" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
```
|
||||
|
||||
### Authentication Systems
|
||||
|
||||
C-Relay supports **two independent authentication systems**:
|
||||
|
||||
#### 1. Auth Rules (Whitelist/Blacklist)
|
||||
- **Config Key**: `auth_enabled=true`
|
||||
- **Purpose**: Block or allow specific pubkeys from publishing events
|
||||
- **Method**: Database-driven rules enforcement
|
||||
- **Use Case**: Spam prevention, content moderation
|
||||
|
||||
#### 2. NIP-42 Cryptographic Authentication
|
||||
- **Config Key**: `nip42_auth_required=true`
|
||||
- **Purpose**: Require cryptographic proof of pubkey ownership
|
||||
- **Method**: Challenge-response authentication protocol
|
||||
- **Use Case**: Verified identity, premium features
|
||||
|
||||
**Important**: These systems can be used independently or together:
|
||||
- `auth_enabled=true` + `nip42_auth_required=false`: Only whitelist/blacklist rules
|
||||
- `auth_enabled=false` + `nip42_auth_required=true`: Only NIP-42 auth challenges
|
||||
- Both `true`: Users must pass NIP-42 auth AND not be blacklisted
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- **Private Key Protection**: Admin private key grants full relay control
|
||||
- **Network Access**: Admin commands should only be sent over secure connections
|
||||
- **Backup**: Keep secure backups of admin private key
|
||||
- **Rotation**: Consider implementing admin key rotation for production deployments
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Common Issues:**
|
||||
- `auth-required: not authorized admin`: Verify you're using the correct admin private key
|
||||
- `invalid: missing or invalid kind`: Ensure event kind is 23455 or 23456
|
||||
- `error: failed to process configuration event`: Check configuration key/value validity
|
||||
- `no valid auth rules found`: Verify tag format uses semicolon syntax for multi-element tags
|
||||
|
||||
**Debug Commands:**
|
||||
```bash
|
||||
# Check if relay is accepting connections
|
||||
echo '["REQ","test",{}]' | websocat ws://localhost:8888
|
||||
|
||||
# Test admin authentication
|
||||
nak event -k 23455 --content "Test auth" \
|
||||
-t "test_config=true" \
|
||||
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
|
||||
|
||||
# Discover available configuration keys
|
||||
nak event -k 23455 --content "Discovery query" \
|
||||
-t "config_query=list_all_keys" \
|
||||
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
|
||||
|
||||
# Query current auth rules
|
||||
nak event -k 23456 --content "Get auth rules" \
|
||||
-t "auth_query=all" \
|
||||
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
|
||||
```
|
||||
|
||||
### Configuration Discovery
|
||||
|
||||
Before making changes, admins can query the relay to discover all available configuration options:
|
||||
|
||||
```bash
|
||||
# Get list of all editable configuration keys with descriptions
|
||||
nak event -k 23455 --content '{"query":"list_config_keys","description":"Discovery"}' \
|
||||
-t "config_query=list_all_keys" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
|
||||
# Get current values of all configuration parameters
|
||||
nak event -k 23455 --content '{"query":"get_config","description":"Current state"}' \
|
||||
-t "config_query=get_current_config" \
|
||||
--sec $ADMIN_PRIVKEY | nak event $RELAY_URL
|
||||
```
|
||||
|
||||
**Expected Response Format:**
|
||||
```json
|
||||
["EVENT", "subscription_id", {
|
||||
"kind": 23455,
|
||||
"content": "{\"config_keys\": [\"auth_enabled\", \"max_connections\", ...], \"descriptions\": {...}}",
|
||||
"tags": [["response_type", "config_keys_list"]]
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"status\": \"error\", \"message\": \"Error: invalid configuration value\"}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
|
||||
**Auth Rules Query Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"auth_rules\", \"total_results\": 2, \"data\": [{\"rule_type\": \"blacklist\", \"pattern_type\": \"pubkey\", \"pattern_value\": \"abc123...\", \"action\": \"deny\"}]}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
|
||||
**Configuration Query Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"config_keys\", \"config_keys\": [\"auth_enabled\", \"max_connections\"], \"descriptions\": {\"auth_enabled\": \"Enable whitelist/blacklist rules\"}}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
|
||||
|
||||
@@ -198,25 +198,54 @@ fi
|
||||
|
||||
echo "Build successful. Proceeding with relay restart..."
|
||||
|
||||
# Kill existing relay if running
|
||||
# Kill existing relay if running - start aggressive immediately
|
||||
echo "Stopping any existing relay servers..."
|
||||
pkill -f "c_relay_" 2>/dev/null
|
||||
sleep 2 # Give time for shutdown
|
||||
|
||||
# Check if port is still bound
|
||||
if lsof -i :8888 >/dev/null 2>&1; then
|
||||
echo "Port 8888 still in use, force killing..."
|
||||
fuser -k 8888/tcp 2>/dev/null || echo "No process on port 8888"
|
||||
# Get all relay processes and kill them immediately with -9
|
||||
RELAY_PIDS=$(pgrep -f "c_relay_" || echo "")
|
||||
if [ -n "$RELAY_PIDS" ]; then
|
||||
echo "Force killing relay processes immediately: $RELAY_PIDS"
|
||||
kill -9 $RELAY_PIDS 2>/dev/null
|
||||
else
|
||||
echo "No existing relay processes found"
|
||||
fi
|
||||
|
||||
# Get any remaining processes
|
||||
REMAINING_PIDS=$(pgrep -f "c_relay_" || echo "")
|
||||
if [ -n "$REMAINING_PIDS" ]; then
|
||||
echo "Force killing remaining processes: $REMAINING_PIDS"
|
||||
kill -9 $REMAINING_PIDS 2>/dev/null
|
||||
# Ensure port 8888 is completely free with retry loop
|
||||
echo "Ensuring port 8888 is available..."
|
||||
for attempt in {1..15}; do
|
||||
if ! lsof -i :8888 >/dev/null 2>&1; then
|
||||
echo "Port 8888 is now free"
|
||||
break
|
||||
fi
|
||||
|
||||
echo "Attempt $attempt: Port 8888 still in use, force killing..."
|
||||
# Kill anything using port 8888
|
||||
fuser -k 8888/tcp 2>/dev/null || true
|
||||
|
||||
# Double-check for any remaining relay processes
|
||||
REMAINING_PIDS=$(pgrep -f "c_relay_" || echo "")
|
||||
if [ -n "$REMAINING_PIDS" ]; then
|
||||
echo "Killing remaining relay processes: $REMAINING_PIDS"
|
||||
kill -9 $REMAINING_PIDS 2>/dev/null || true
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
|
||||
if [ $attempt -eq 15 ]; then
|
||||
echo "ERROR: Could not free port 8888 after 15 attempts"
|
||||
echo "Current processes using port:"
|
||||
lsof -i :8888 2>/dev/null || echo "No process details available"
|
||||
echo "You may need to manually kill processes or reboot"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Final safety check - ensure no relay processes remain
|
||||
FINAL_PIDS=$(pgrep -f "c_relay_" || echo "")
|
||||
if [ -n "$FINAL_PIDS" ]; then
|
||||
echo "Final cleanup: killing processes $FINAL_PIDS"
|
||||
kill -9 $FINAL_PIDS 2>/dev/null || true
|
||||
sleep 1
|
||||
else
|
||||
echo "No existing relay found"
|
||||
fi
|
||||
|
||||
# Clean up PID file
|
||||
|
||||
949
src/config.c
949
src/config.c
File diff suppressed because it is too large
Load Diff
19
src/config.h
19
src/config.h
@@ -6,6 +6,9 @@
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
// Forward declaration for WebSocket support
|
||||
struct lws;
|
||||
|
||||
// Configuration constants
|
||||
#define CONFIG_VALUE_MAX_LENGTH 1024
|
||||
#define RELAY_NAME_MAX_LENGTH 256
|
||||
@@ -160,10 +163,20 @@ int update_config_in_table(const char* key, const char* value);
|
||||
int populate_default_config_values(void);
|
||||
int add_pubkeys_to_config_table(void);
|
||||
|
||||
// Admin event processing functions
|
||||
int process_admin_event_in_config(cJSON* event, char* error_message, size_t error_size);
|
||||
// Admin event processing functions (updated with WebSocket support)
|
||||
int process_admin_event_in_config(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
|
||||
int process_admin_config_event(cJSON* event, char* error_message, size_t error_size);
|
||||
int process_admin_auth_event(cJSON* event, char* error_message, size_t error_size);
|
||||
int process_admin_auth_event(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
|
||||
|
||||
// Unified Kind 23456 handler functions
|
||||
int handle_kind_23456_unified(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
|
||||
int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_message, size_t error_size, struct lws* wsi);
|
||||
int handle_system_command_unified(cJSON* event, const char* command, char* error_message, size_t error_size, struct lws* wsi);
|
||||
int handle_auth_rule_modification_unified(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// WebSocket response functions
|
||||
int send_websocket_response_data(cJSON* event, cJSON* response_data, struct lws* wsi);
|
||||
cJSON* build_query_response(const char* query_type, cJSON* results_array, int total_count);
|
||||
|
||||
// Auth rules management functions
|
||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||
|
||||
197
src/main.c
197
src/main.c
@@ -228,7 +228,10 @@ int nostr_validate_unified_request(const char* json_string, size_t json_length);
|
||||
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for admin event processing (kinds 33334 and 33335)
|
||||
int process_admin_event_in_config(cJSON* event, char* error_message, size_t error_size);
|
||||
int process_admin_event_in_config(cJSON* event, char* error_message, size_t error_size, struct lws* wsi);
|
||||
|
||||
// Forward declaration for enhanced admin event authorization
|
||||
int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, const char* message);
|
||||
@@ -3009,6 +3012,109 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
|
||||
return events_sent;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ADMIN EVENT AUTHORIZATION
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Enhanced admin event authorization function
|
||||
int is_authorized_admin_event(cJSON* event, char* error_buffer, size_t error_buffer_size) {
|
||||
if (!event || !error_buffer) {
|
||||
if (error_buffer && error_buffer_size > 0) {
|
||||
snprintf(error_buffer, error_buffer_size, "Invalid parameters for admin authorization");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 1: Verify event kind is admin type
|
||||
cJSON *kind_json = cJSON_GetObjectItem(event, "kind");
|
||||
if (!kind_json || !cJSON_IsNumber(kind_json)) {
|
||||
snprintf(error_buffer, error_buffer_size, "Missing or invalid event kind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int event_kind = kind_json->valueint;
|
||||
if (event_kind != 33334 && event_kind != 33335 && event_kind != 23455 && event_kind != 23456) {
|
||||
snprintf(error_buffer, error_buffer_size, "Event kind %d is not an admin event type", event_kind);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 2: Check if event targets this relay (look for 'p' tag with our relay pubkey)
|
||||
cJSON *tags = cJSON_GetObjectItem(event, "tags");
|
||||
if (!tags || !cJSON_IsArray(tags)) {
|
||||
// No tags array - treat as regular event for different relay
|
||||
log_info("Admin event has no tags array - treating as event for different relay");
|
||||
snprintf(error_buffer, error_buffer_size, "Admin event not targeting this relay (no tags)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int targets_this_relay = 0;
|
||||
cJSON *tag;
|
||||
cJSON_ArrayForEach(tag, tags) {
|
||||
if (cJSON_IsArray(tag)) {
|
||||
cJSON *tag_name = cJSON_GetArrayItem(tag, 0);
|
||||
cJSON *tag_value = cJSON_GetArrayItem(tag, 1);
|
||||
|
||||
if (tag_name && cJSON_IsString(tag_name) &&
|
||||
tag_value && cJSON_IsString(tag_value) &&
|
||||
strcmp(tag_name->valuestring, "p") == 0) {
|
||||
|
||||
// Compare with our relay pubkey
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
if (relay_pubkey && strcmp(tag_value->valuestring, relay_pubkey) == 0) {
|
||||
targets_this_relay = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!targets_this_relay) {
|
||||
// Admin event for different relay - not an error, just not for us
|
||||
log_info("Admin event targets different relay - treating as regular event");
|
||||
snprintf(error_buffer, error_buffer_size, "Admin event not targeting this relay");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 3: Verify admin signature authorization
|
||||
cJSON *pubkey_json = cJSON_GetObjectItem(event, "pubkey");
|
||||
if (!pubkey_json || !cJSON_IsString(pubkey_json)) {
|
||||
log_warning("Unauthorized admin event attempt: missing or invalid pubkey");
|
||||
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: missing pubkey");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get admin pubkey from configuration
|
||||
const char* admin_pubkey = get_config_value("admin_pubkey");
|
||||
if (!admin_pubkey || strlen(admin_pubkey) == 0) {
|
||||
log_warning("Unauthorized admin event attempt: no admin pubkey configured");
|
||||
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: no admin configured");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Compare pubkeys
|
||||
if (strcmp(pubkey_json->valuestring, admin_pubkey) != 0) {
|
||||
log_warning("Unauthorized admin event attempt: pubkey mismatch");
|
||||
char warning_msg[256];
|
||||
snprintf(warning_msg, sizeof(warning_msg),
|
||||
"Unauthorized admin event attempt from pubkey: %.32s...", pubkey_json->valuestring);
|
||||
log_warning(warning_msg);
|
||||
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: invalid admin pubkey");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 4: Verify event signature
|
||||
if (nostr_verify_event_signature(event) != 0) {
|
||||
log_warning("Unauthorized admin event attempt: invalid signature");
|
||||
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: signature verification failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// All checks passed - authorized admin event
|
||||
log_info("Admin event authorization successful");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3266,47 +3372,68 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
}
|
||||
|
||||
if (event_kind == 33334 || event_kind == 33335 || event_kind == 23455 || event_kind == 23456) {
|
||||
// This is an admin event - process it through the admin API instead of normal storage
|
||||
log_info("DEBUG ADMIN: Admin event detected, processing through admin API");
|
||||
// Enhanced admin event security - check authorization first
|
||||
log_info("DEBUG ADMIN: Admin event detected, checking authorization");
|
||||
|
||||
char admin_error[512] = {0};
|
||||
int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error));
|
||||
char auth_error[512] = {0};
|
||||
int auth_result = is_authorized_admin_event(event, auth_error, sizeof(auth_error));
|
||||
|
||||
char debug_admin_msg[256];
|
||||
snprintf(debug_admin_msg, sizeof(debug_admin_msg),
|
||||
"DEBUG ADMIN: process_admin_event_in_config returned %d", admin_result);
|
||||
log_info(debug_admin_msg);
|
||||
|
||||
// Log results for Kind 23455 and 23456 events
|
||||
if (event_kind == 23455 || event_kind == 23456) {
|
||||
if (admin_result == 0) {
|
||||
char success_result_msg[256];
|
||||
snprintf(success_result_msg, sizeof(success_result_msg),
|
||||
"SUCCESS: Kind %d event processed successfully", event_kind);
|
||||
log_success(success_result_msg);
|
||||
} else {
|
||||
char error_result_msg[512];
|
||||
snprintf(error_result_msg, sizeof(error_result_msg),
|
||||
"ERROR: Kind %d event processing failed: %s", event_kind, admin_error);
|
||||
log_error(error_result_msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (admin_result != 0) {
|
||||
log_error("DEBUG ADMIN: Failed to process admin event through admin API");
|
||||
if (auth_result != 0) {
|
||||
// Authorization failed - log and reject
|
||||
log_warning("DEBUG ADMIN: Admin event authorization failed");
|
||||
result = -1;
|
||||
size_t error_len = strlen(admin_error);
|
||||
size_t error_len = strlen(auth_error);
|
||||
size_t copy_len = (error_len < sizeof(error_message) - 1) ? error_len : sizeof(error_message) - 1;
|
||||
memcpy(error_message, admin_error, copy_len);
|
||||
memcpy(error_message, auth_error, copy_len);
|
||||
error_message[copy_len] = '\0';
|
||||
|
||||
char debug_admin_error_msg[600];
|
||||
snprintf(debug_admin_error_msg, sizeof(debug_admin_error_msg),
|
||||
"DEBUG ADMIN ERROR: %.400s", admin_error);
|
||||
log_error(debug_admin_error_msg);
|
||||
char debug_auth_error_msg[600];
|
||||
snprintf(debug_auth_error_msg, sizeof(debug_auth_error_msg),
|
||||
"DEBUG ADMIN AUTH ERROR: %.400s", auth_error);
|
||||
log_warning(debug_auth_error_msg);
|
||||
} else {
|
||||
log_success("DEBUG ADMIN: Admin event processed successfully through admin API");
|
||||
// Admin events are processed by the admin API, not broadcast to subscriptions
|
||||
// Authorization successful - process through admin API
|
||||
log_info("DEBUG ADMIN: Admin event authorized, processing through admin API");
|
||||
|
||||
char admin_error[512] = {0};
|
||||
int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi);
|
||||
|
||||
char debug_admin_msg[256];
|
||||
snprintf(debug_admin_msg, sizeof(debug_admin_msg),
|
||||
"DEBUG ADMIN: process_admin_event_in_config returned %d", admin_result);
|
||||
log_info(debug_admin_msg);
|
||||
|
||||
// Log results for Kind 23455 and 23456 events
|
||||
if (event_kind == 23455 || event_kind == 23456) {
|
||||
if (admin_result == 0) {
|
||||
char success_result_msg[256];
|
||||
snprintf(success_result_msg, sizeof(success_result_msg),
|
||||
"SUCCESS: Kind %d event processed successfully", event_kind);
|
||||
log_success(success_result_msg);
|
||||
} else {
|
||||
char error_result_msg[512];
|
||||
snprintf(error_result_msg, sizeof(error_result_msg),
|
||||
"ERROR: Kind %d event processing failed: %s", event_kind, admin_error);
|
||||
log_error(error_result_msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (admin_result != 0) {
|
||||
log_error("DEBUG ADMIN: Failed to process admin event through admin API");
|
||||
result = -1;
|
||||
size_t error_len = strlen(admin_error);
|
||||
size_t copy_len = (error_len < sizeof(error_message) - 1) ? error_len : sizeof(error_message) - 1;
|
||||
memcpy(error_message, admin_error, copy_len);
|
||||
error_message[copy_len] = '\0';
|
||||
|
||||
char debug_admin_error_msg[600];
|
||||
snprintf(debug_admin_error_msg, sizeof(debug_admin_error_msg),
|
||||
"DEBUG ADMIN ERROR: %.400s", admin_error);
|
||||
log_error(debug_admin_error_msg);
|
||||
} else {
|
||||
log_success("DEBUG ADMIN: Admin event processed successfully through admin API");
|
||||
// Admin events are processed by the admin API, not broadcast to subscriptions
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Regular event - store in database and broadcast
|
||||
|
||||
@@ -34,6 +34,13 @@ RELAY_URL="ws://${RELAY_HOST}:${RELAY_PORT}"
|
||||
TIMEOUT=5
|
||||
TEMP_DIR="/tmp/c_relay_test_$$"
|
||||
|
||||
# WebSocket connection state
|
||||
WS_PID=""
|
||||
WS_INPUT_FIFO=""
|
||||
WS_OUTPUT_FIFO=""
|
||||
WS_CONNECTED=0
|
||||
WS_RESPONSE_LOG=""
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
@@ -147,6 +154,145 @@ send_websocket_message() {
|
||||
echo "$response"
|
||||
}
|
||||
|
||||
# =======================================================================
|
||||
# PERSISTENT WEBSOCKET CONNECTION MANAGEMENT
|
||||
# =======================================================================
|
||||
|
||||
# Open persistent WebSocket connection
|
||||
open_websocket_connection() {
|
||||
log_info "Opening persistent WebSocket connection to $RELAY_URL..."
|
||||
|
||||
# Create unique named pipes for this test session
|
||||
WS_INPUT_FIFO="${TEMP_DIR}/ws_input_$$"
|
||||
WS_OUTPUT_FIFO="${TEMP_DIR}/ws_output_$$"
|
||||
WS_RESPONSE_LOG="${TEMP_DIR}/ws_responses_$$"
|
||||
|
||||
# Create named pipes
|
||||
mkfifo "$WS_INPUT_FIFO" "$WS_OUTPUT_FIFO"
|
||||
|
||||
# Start websocat in background with bidirectional pipes
|
||||
# Input: we write to WS_INPUT_FIFO, websocat reads and sends to relay
|
||||
# Output: websocat receives from relay and writes to WS_OUTPUT_FIFO
|
||||
websocat "$RELAY_URL" < "$WS_INPUT_FIFO" > "$WS_OUTPUT_FIFO" &
|
||||
WS_PID=$!
|
||||
|
||||
# Start background response logger
|
||||
tail -f "$WS_OUTPUT_FIFO" >> "$WS_RESPONSE_LOG" &
|
||||
local logger_pid=$!
|
||||
|
||||
# Keep input pipe open by redirecting from /dev/null in background
|
||||
exec {ws_fd}> "$WS_INPUT_FIFO"
|
||||
|
||||
# Test connection with a simple REQ message
|
||||
sleep 1
|
||||
echo '["REQ","test_conn",{}]' >&${ws_fd}
|
||||
|
||||
# Wait for response to confirm connection
|
||||
local connection_timeout=5
|
||||
local start_time=$(date +%s)
|
||||
|
||||
while [ $(($(date +%s) - start_time)) -lt $connection_timeout ]; do
|
||||
if [ -s "$WS_RESPONSE_LOG" ]; then
|
||||
WS_CONNECTED=1
|
||||
log_success "Persistent WebSocket connection established"
|
||||
log_info "WebSocket PID: $WS_PID"
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Connection failed
|
||||
log_error "Failed to establish persistent WebSocket connection"
|
||||
close_websocket_connection
|
||||
return 1
|
||||
}
|
||||
|
||||
# Close persistent WebSocket connection
|
||||
close_websocket_connection() {
|
||||
log_info "Closing persistent WebSocket connection..."
|
||||
|
||||
if [ -n "$WS_PID" ] && kill -0 "$WS_PID" 2>/dev/null; then
|
||||
# Close input pipe first
|
||||
if [ -n "${ws_fd}" ]; then
|
||||
exec {ws_fd}>&-
|
||||
fi
|
||||
|
||||
# Send close frame and terminate websocat
|
||||
kill "$WS_PID" 2>/dev/null
|
||||
wait "$WS_PID" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Kill any remaining background processes
|
||||
pkill -f "tail -f.*$WS_OUTPUT_FIFO" 2>/dev/null || true
|
||||
|
||||
# Clean up pipes
|
||||
[ -p "$WS_INPUT_FIFO" ] && rm -f "$WS_INPUT_FIFO"
|
||||
[ -p "$WS_OUTPUT_FIFO" ] && rm -f "$WS_OUTPUT_FIFO"
|
||||
|
||||
WS_PID=""
|
||||
WS_CONNECTED=0
|
||||
|
||||
log_info "WebSocket connection closed"
|
||||
}
|
||||
|
||||
# Send event through persistent WebSocket connection
|
||||
send_websocket_event() {
|
||||
local event_json="$1"
|
||||
local timeout_seconds="${2:-10}"
|
||||
|
||||
if [ "$WS_CONNECTED" != "1" ]; then
|
||||
log_error "WebSocket connection not established"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Clear previous responses
|
||||
> "$WS_RESPONSE_LOG"
|
||||
|
||||
# Create EVENT message
|
||||
local event_message="[\"EVENT\",$event_json]"
|
||||
|
||||
# Send through persistent connection
|
||||
echo "$event_message" >&${ws_fd}
|
||||
|
||||
# Wait for OK response
|
||||
local start_time=$(date +%s)
|
||||
while [ $(($(date +%s) - start_time)) -lt $timeout_seconds ]; do
|
||||
if grep -q '"OK"' "$WS_RESPONSE_LOG" 2>/dev/null; then
|
||||
local response=$(tail -1 "$WS_RESPONSE_LOG")
|
||||
echo "$response"
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
log_error "Timeout waiting for WebSocket response"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Wait for query response data from relay
|
||||
wait_for_query_response() {
|
||||
local timeout_seconds="${1:-10}"
|
||||
local start_time=$(date +%s)
|
||||
|
||||
log_info "Waiting for query response data..."
|
||||
|
||||
# Clear any OK responses and wait for JSON data
|
||||
sleep 0.5 # Brief delay to ensure OK response is processed first
|
||||
|
||||
while [ $(($(date +%s) - start_time)) -lt $timeout_seconds ]; do
|
||||
# Look for JSON response with query data (not just OK responses)
|
||||
if grep -q '"query_type"' "$WS_RESPONSE_LOG" 2>/dev/null; then
|
||||
local response=$(grep '"query_type"' "$WS_RESPONSE_LOG" | tail -1)
|
||||
echo "$response"
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
log_error "Timeout waiting for query response data"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Create and send auth rule event
|
||||
send_auth_rule_event() {
|
||||
local action="$1" # "add" or "remove"
|
||||
@@ -157,12 +303,13 @@ send_auth_rule_event() {
|
||||
|
||||
log_info "Creating auth rule event: $action $rule_type $pattern_type ${pattern_value:0:16}..."
|
||||
|
||||
# Create the auth rule event using nak with correct tag format
|
||||
# Server expects proper key=value tags for auth rules
|
||||
# Using Kind 23456 (ephemeral auth rules management) - no d tag needed
|
||||
# Create the auth rule event using nak with correct tag format for the actual implementation
|
||||
# Server expects tags like ["whitelist", "pubkey", "abc123..."] or ["blacklist", "pubkey", "def456..."]
|
||||
# Using Kind 23456 (ephemeral auth rules management) with proper relay targeting
|
||||
local event_json
|
||||
event_json=$(nak event -k 23456 --content "{\"action\":\"$action\",\"description\":\"$description\"}" \
|
||||
-t "$rule_type=$pattern_type" -t "pattern_value=$pattern_value" \
|
||||
event_json=$(nak event -k 23456 --content "" \
|
||||
-t "p=$RELAY_PUBKEY" \
|
||||
-t "$rule_type=$pattern_type=$pattern_value" \
|
||||
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ] || [ -z "$event_json" ]; then
|
||||
@@ -170,21 +317,38 @@ send_auth_rule_event() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Send the event using nak directly to relay (more reliable than websocat)
|
||||
# Send the event through persistent WebSocket connection
|
||||
log_info "Publishing auth rule event to relay..."
|
||||
local result
|
||||
result=$(echo "$event_json" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||
local exit_code=$?
|
||||
if [ "$WS_CONNECTED" = "1" ]; then
|
||||
result=$(send_websocket_event "$event_json")
|
||||
local exit_code=$?
|
||||
|
||||
log_info "Auth rule event result: $result"
|
||||
log_info "Auth rule event result: $result"
|
||||
|
||||
# Check if response indicates success
|
||||
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i "success\|OK.*true\|published"; then
|
||||
log_success "Auth rule $action successful"
|
||||
return 0
|
||||
# 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
|
||||
else
|
||||
log_error "Auth rule $action failed: $result (exit code: $exit_code)"
|
||||
return 1
|
||||
# Fallback to one-shot connection if persistent connection not available
|
||||
result=$(echo "$event_json" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||
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 "success\|OK.*true\|published"; then
|
||||
log_success "Auth rule $action successful"
|
||||
return 0
|
||||
else
|
||||
log_error "Auth rule $action failed: $result (exit code: $exit_code)"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -193,9 +357,10 @@ clear_all_auth_rules() {
|
||||
log_info "Clearing all existing auth rules..."
|
||||
|
||||
# Create system command event to clear all auth rules
|
||||
# Using Kind 23456 (ephemeral auth rules management)
|
||||
# Using Kind 23456 (ephemeral auth rules management) with proper relay targeting
|
||||
local event_json
|
||||
event_json=$(nak event -k 23456 --content "{\"action\":\"clear_all\"}" \
|
||||
event_json=$(nak event -k 23456 --content "" \
|
||||
-t "p=$RELAY_PUBKEY" \
|
||||
-t "system_command=clear_all_auth_rules" \
|
||||
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
|
||||
|
||||
@@ -204,21 +369,38 @@ clear_all_auth_rules() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Send the event using nak directly to relay
|
||||
# Send the event through persistent WebSocket connection
|
||||
log_info "Sending clear all auth rules command..."
|
||||
local result
|
||||
result=$(echo "$event_json" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||
local exit_code=$?
|
||||
if [ "$WS_CONNECTED" = "1" ]; then
|
||||
result=$(send_websocket_event "$event_json")
|
||||
local exit_code=$?
|
||||
|
||||
log_info "Clear auth rules result: $result"
|
||||
log_info "Clear auth rules result: $result"
|
||||
|
||||
# Check if response indicates success
|
||||
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i "success\|OK.*true\|published"; then
|
||||
log_success "All auth rules cleared successfully"
|
||||
return 0
|
||||
# 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
|
||||
else
|
||||
log_error "Failed to clear auth rules: $result (exit code: $exit_code)"
|
||||
return 1
|
||||
# Fallback to one-shot connection if persistent connection not available
|
||||
result=$(echo "$event_json" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||
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 "success\|OK.*true\|published"; 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
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -324,11 +506,11 @@ test_admin_authentication() {
|
||||
log "Test $TESTS_RUN: Admin Authentication"
|
||||
|
||||
# Create a simple configuration event to test admin authentication
|
||||
# Using Kind 23455 (ephemeral configuration management) - no d tag needed
|
||||
local content="{\"action\":\"set\",\"description\":\"Testing admin authentication\"}"
|
||||
# Using Kind 23456 (admin commands) with proper relay targeting
|
||||
local config_event
|
||||
config_event=$(nak event -k 23455 --content "$content" \
|
||||
-t "test_auth=true" \
|
||||
config_event=$(nak event -k 23456 --content "" \
|
||||
-t "p=$RELAY_PUBKEY" \
|
||||
-t "system_command=system_status" \
|
||||
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
@@ -367,8 +549,9 @@ test_auth_rules_storage_query() {
|
||||
# Query all auth rules using admin query
|
||||
log_info "Querying all auth rules..."
|
||||
local query_event
|
||||
query_event=$(nak event -k 23456 --content "{\"action\":\"list_all\"}" \
|
||||
-t "auth_query=list_all" \
|
||||
query_event=$(nak event -k 23456 --content "" \
|
||||
-t "p=$RELAY_PUBKEY" \
|
||||
-t "auth_query=all" \
|
||||
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ] || [ -z "$query_event" ]; then
|
||||
@@ -697,11 +880,11 @@ run_all_tests() {
|
||||
# Setup
|
||||
setup_test_environment
|
||||
|
||||
# Clear all auth rules before starting tests
|
||||
|
||||
clear_all_auth_rules
|
||||
|
||||
# test_admin_authentication
|
||||
# test_auth_rules_storage_query
|
||||
test_admin_authentication
|
||||
test_auth_rules_storage_query
|
||||
# test_basic_whitelist
|
||||
# test_basic_blacklist
|
||||
# test_rule_removal
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
=== C-Relay Whitelist/Blacklist Test Started at Thu Sep 25 07:28:40 AM EDT 2025 ===
|
||||
[0;34m[INFO][0m Checking dependencies...
|
||||
[0;32m[SUCCESS][0m Dependencies check complete
|
||||
[0;34m[INFO][0m Generated keypair for TEST1: pubkey=36e6521000b2ddda...
|
||||
[0;34m[INFO][0m Generated keypair for TEST2: pubkey=9cdd32f27fffeea8...
|
||||
[0;34m[INFO][0m Generated keypair for TEST3: pubkey=e05928b64d3ad54a...
|
||||
[0;32m[SUCCESS][0m Test environment setup complete
|
||||
[0;34m[07:28:42][0m Test 1: Admin Authentication
|
||||
[0;34m[INFO][0m === DEBUG: Full admin event being sent ===
|
||||
[0;34m[INFO][0m === END DEBUG EVENT ===
|
||||
[0;34m[INFO][0m === DEBUG: Full WebSocket message ===
|
||||
[0;34m[INFO][0m === END DEBUG MESSAGE ===
|
||||
[0;34m[INFO][0m Sending WebSocket message (full):
|
||||
[0;34m[INFO][0m ["EVENT",{"kind":33334,"id":"ba07a9d01ef3bf8c424eb5ecd8a162980e5d596f3c8520ea59c4cf80961a347a","pubkey":"6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3","created_at":1758799722,"tags":[["d","4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"],["test_auth","true"]],"content":"Testing admin authentication","sig":"b8f40558060f402da232f3cc2ff72ea98257a3e3d74bc5c4bebd6fbd24d8d258138f879795b6089c01c4afa4c64088306dc917fdcad7b054d3c12513581b9228"}]
|
||||
[0;34m[INFO][0m WebSocket response (full):
|
||||
[0;34m[INFO][0m
|
||||
[0;34m[INFO][0m === DEBUG: Full server response ===
|
||||
[0;34m[INFO][0m === END DEBUG RESPONSE ===
|
||||
[0;31m[ERROR][0m Test 1: FAILED - Admin authentication failed: [0;34m[INFO][0m Sending WebSocket message (full):
|
||||
[0;34m[INFO][0m ["EVENT",{"kind":33334,"id":"ba07a9d01ef3bf8c424eb5ecd8a162980e5d596f3c8520ea59c4cf80961a347a","pubkey":"6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3","created_at":1758799722,"tags":[["d","4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"],["test_auth","true"]],"content":"Testing admin authentication","sig":"b8f40558060f402da232f3cc2ff72ea98257a3e3d74bc5c4bebd6fbd24d8d258138f879795b6089c01c4afa4c64088306dc917fdcad7b054d3c12513581b9228"}]
|
||||
[0;34m[INFO][0m WebSocket response (full):
|
||||
[0;34m[INFO][0m
|
||||
[0;31m[ERROR][0m 1 out of 1 tests failed.
|
||||
[0;31m[ERROR][0m Some tests failed. Check the log for details.
|
||||
[0;34m[07:28:42][0m Cleaning up test environment...
|
||||
[0;34m[INFO][0m Temporary directory removed: /tmp/c_relay_test_184904
|
||||
[0;34m[07:28:42][0m Test cleanup completed.
|
||||
Reference in New Issue
Block a user