v0.0.14 - Still working on commenting out old code and deleting

This commit is contained in:
Your Name
2025-09-11 07:48:33 -04:00
parent cafaa2f2e2
commit e1741fb1fc
10 changed files with 225 additions and 22 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -15,7 +15,7 @@
#define DB_PATH "db/ginxsom.db" #define DB_PATH "db/ginxsom.db"
// Function declarations (moved from admin_api.h) // Function declarations (moved from admin_api.h)
void handle_admin_api_request(const char* method, const char* uri); void handle_admin_api_request(const char* method, const char* uri, const char* validated_pubkey, int is_authenticated);
void handle_stats_api(void); void handle_stats_api(void);
void handle_config_get_api(void); void handle_config_get_api(void);
void handle_config_put_api(void); void handle_config_put_api(void);
@@ -120,7 +120,7 @@ static const char* admin_mime_to_extension(const char* mime_type) {
} }
// Main API request handler // Main API request handler
void handle_admin_api_request(const char* method, const char* uri) { void handle_admin_api_request(const char* method, const char* uri, const char* validated_pubkey, int is_authenticated) {
const char* path = uri + 4; // Skip "/api" const char* path = uri + 4; // Skip "/api"
// Check if admin interface is enabled // Check if admin interface is enabled
@@ -129,15 +129,20 @@ void handle_admin_api_request(const char* method, const char* uri) {
return; return;
} }
// TODO: Re-enable authentication later // Authentication now handled by centralized validation system
// Authentication temporarily disabled for testing // Health endpoint is exempt from authentication requirement
// if (strcmp(path, "/health") != 0) { if (strcmp(path, "/health") != 0) {
// const char* auth_header = getenv("HTTP_AUTHORIZATION"); if (!is_authenticated || !validated_pubkey) {
// if (!authenticate_admin_request(auth_header)) { send_json_error(401, "admin_auth_required", "Valid admin authentication required");
// send_json_error(401, "admin_auth_required", "Valid admin authentication required"); return;
// return; }
// }
// } // Verify the authenticated pubkey has admin privileges
if (!verify_admin_pubkey(validated_pubkey)) {
send_json_error(403, "admin_forbidden", "Admin privileges required");
return;
}
}
// Route to appropriate handler // Route to appropriate handler
if (strcmp(method, "GET") == 0) { if (strcmp(method, "GET") == 0) {

View File

@@ -251,7 +251,7 @@ int validate_sha256_format(const char* sha256);
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
// Admin API request handler // Admin API request handler
void handle_admin_api_request(const char* method, const char* uri); void handle_admin_api_request(const char* method, const char* uri, const char* validated_pubkey, int is_authenticated);
// Individual endpoint handlers // Individual endpoint handlers
void handle_stats_api(void); void handle_stats_api(void);

View File

@@ -1024,6 +1024,8 @@ void handle_upload_request(void) {
fflush(stderr); fflush(stderr);
// Legacy authentication check - now handled by centralized validation system
/*
// Check if authentication rules are enabled using nostr_core_lib system // Check if authentication rules are enabled using nostr_core_lib system
int auth_required = nostr_auth_rules_enabled(); int auth_required = nostr_auth_rules_enabled();
fprintf(stderr, "AUTH: auth_rules_enabled = %d, auth_header present: %s\r\n", fprintf(stderr, "AUTH: auth_rules_enabled = %d, auth_header present: %s\r\n",
@@ -1046,12 +1048,14 @@ void handle_upload_request(void) {
// Skip validation and proceed to file processing // Skip validation and proceed to file processing
goto process_file_upload; goto process_file_upload;
} }
*/
// Authentication is handled by centralized validation system // Authentication is handled by centralized validation system
// TODO: Get uploader_pubkey from centralized validation result // TODO: Get uploader_pubkey from centralized validation result
// For now, keep existing uploader_pubkey extraction for compatibility // For now, keep existing uploader_pubkey extraction for compatibility
process_file_upload: // Legacy goto label - no longer needed with centralized validation
// process_file_upload:
// Get dimensions from in-memory buffer before saving file // Get dimensions from in-memory buffer before saving file
int width = 0, height = 0; int width = 0, height = 0;
nip94_get_dimensions(file_data, content_length, content_type, &width, nip94_get_dimensions(file_data, content_length, content_type, &width,
@@ -1295,6 +1299,8 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
fflush(stderr); fflush(stderr);
// Legacy authentication check - now handled by centralized validation system
/*
// Check if authentication rules are enabled using nostr_core_lib system // Check if authentication rules are enabled using nostr_core_lib system
int auth_required = nostr_auth_rules_enabled(); int auth_required = nostr_auth_rules_enabled();
fprintf(stderr, "AUTH: auth_rules_enabled = %d, auth_header present: %s\r\n", fprintf(stderr, "AUTH: auth_rules_enabled = %d, auth_header present: %s\r\n",
@@ -1317,11 +1323,13 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
// Skip validation and proceed to file processing // Skip validation and proceed to file processing
goto process_file_upload; goto process_file_upload;
} }
*/
// Authentication was handled by centralized validation system // Authentication was handled by centralized validation system
// uploader_pubkey should be set from validation result // uploader_pubkey should be set from validation result
process_file_upload: // Legacy goto label - no longer needed with centralized validation
// process_file_upload:
// Get dimensions from in-memory buffer before saving file // Get dimensions from in-memory buffer before saving file
int width = 0, height = 0; int width = 0, height = 0;
nip94_get_dimensions(file_data, file_size, content_type, &width, nip94_get_dimensions(file_data, file_size, content_type, &width,
@@ -1650,6 +1658,8 @@ if (!config_loaded /* && !initialize_server_config() */) {
// HEAD requests might not require auth depending on config - let handler decide // HEAD requests might not require auth depending on config - let handler decide
} else if (strcmp(operation, "list") == 0) { } else if (strcmp(operation, "list") == 0) {
// List operation might be optional auth - let handler decide // List operation might be optional auth - let handler decide
} else if (strcmp(operation, "admin") == 0 && strcmp(request_uri, "/api/health") == 0) {
// Health endpoint is public and doesn't require authentication - let handler decide
} else { } else {
// For other operations, validation failure means auth failure // For other operations, validation failure means auth failure
const char *message = result.reason[0] ? result.reason : "Authentication failed"; const char *message = result.reason[0] ? result.reason : "Authentication failed";
@@ -1732,8 +1742,8 @@ if (!config_loaded /* && !initialize_server_config() */) {
} else if (strncmp(request_uri, "/api/", 5) == 0) { } else if (strncmp(request_uri, "/api/", 5) == 0) {
// Handle admin API requests with pre-validated auth // Handle admin API requests with pre-validated auth
// TODO: Pass validated result to existing handler const char *validated_pubkey = (result.valid && strlen(result.pubkey) == 64) ? result.pubkey : NULL;
handle_admin_api_request(request_method, request_uri); handle_admin_api_request(request_method, request_uri, validated_pubkey, result.valid);
} else if (strcmp(request_method, "GET") == 0 && } else if (strcmp(request_method, "GET") == 0 &&

View File

@@ -113,6 +113,7 @@ static int validate_blossom_event(cJSON *event, const char *expected_hash,
const char *method); const char *method);
static int validate_nip42_event(cJSON *event, const char *relay_url, static int validate_nip42_event(cJSON *event, const char *relay_url,
const char *challenge_id); const char *challenge_id);
static int validate_admin_event(cJSON *event, const char *method, const char *endpoint);
static int check_database_auth_rules(const char *pubkey, const char *operation, static int check_database_auth_rules(const char *pubkey, const char *operation,
const char *resource_hash); const char *resource_hash);
void nostr_request_validator_clear_violation(void); void nostr_request_validator_clear_violation(void);
@@ -659,6 +660,79 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
"VALIDATOR_DEBUG: STEP 10 PASSED - Blossom authentication succeeded\n"); "VALIDATOR_DEBUG: STEP 10 PASSED - Blossom authentication succeeded\n");
strcpy(result->reason, "Blossom authentication passed"); strcpy(result->reason, "Blossom authentication passed");
} else if (event_kind == 33335) {
// 10. Admin/Configuration Event Validation (Kind 33335)
// Verify admin authorization, check required tags, validate expiration
validator_debug_log("VALIDATOR_DEBUG: STEP 10 - Processing Admin/Configuration "
"authentication (kind 33335)\n");
char admin_valid_msg[512];
sprintf(admin_valid_msg,
"VALIDATOR_DEBUG: Validating Admin event for operation='%s', "
"endpoint='%s'\n",
request->operation ? request->operation : "NULL",
request->resource_hash ? request->resource_hash : "admin_api");
validator_debug_log(admin_valid_msg);
// For admin operations, we pass the HTTP method and API endpoint
const char *admin_method = NULL;
const char *admin_endpoint = NULL;
// Extract method and endpoint from request context
// For admin API, we need to get the actual HTTP method and full endpoint
if (request->operation && strcmp(request->operation, "admin") == 0) {
// Admin events should contain method and endpoint tags that match the actual request
// We don't enforce specific values here - let the event tags drive the validation
admin_method = NULL; // Let validation check event tags without enforcing specific method
admin_endpoint = NULL; // Let validation check event tags without enforcing specific endpoint
}
int admin_result = validate_admin_event(event, admin_method, admin_endpoint);
if (admin_result != NOSTR_SUCCESS) {
char admin_fail_msg[256];
sprintf(admin_fail_msg,
"VALIDATOR_DEBUG: STEP 10 FAILED - Admin validation failed "
"(error=%d)\n",
admin_result);
validator_debug_log(admin_fail_msg);
result->valid = 0;
result->error_code = admin_result;
// Map specific Admin error codes to detailed error messages
switch (admin_result) {
case NOSTR_ERROR_EVENT_EXPIRED:
strcpy(result->reason, "Admin authorization event has expired. Create a new signed event with future expiration.");
break;
case NOSTR_ERROR_EVENT_INVALID_CONTENT:
strcpy(result->reason, "Admin event missing required tags. Admin events need 'method' and 'endpoint' tags.");
break;
case NOSTR_ERROR_EVENT_INVALID_TAGS:
strcpy(result->reason, "Invalid or missing admin tags. Check 'method' tag matches HTTP method and 'endpoint' tag is valid.");
break;
case NOSTR_ERROR_EVENT_INVALID_SIGNATURE:
strcpy(result->reason, "Admin event signature verification failed. Check private key and event serialization.");
break;
case NOSTR_ERROR_EVENT_INVALID_KIND:
strcpy(result->reason, "Invalid event kind. Admin authorization events must use kind 33335.");
break;
case NOSTR_ERROR_AUTH_REQUIRED:
strcpy(result->reason, "Admin access denied. Pubkey is not authorized for admin operations.");
break;
default:
snprintf(result->reason, sizeof(result->reason),
"Admin event does not authorize this operation (error code: %d). Check tags and admin permissions.",
admin_result);
break;
}
cJSON_Delete(event);
return NOSTR_SUCCESS;
}
validator_debug_log(
"VALIDATOR_DEBUG: STEP 10 PASSED - Admin authentication succeeded\n");
strcpy(result->reason, "Admin authentication passed");
} else { } else {
char unsupported_msg[256]; char unsupported_msg[256];
sprintf(unsupported_msg, sprintf(unsupported_msg,
@@ -668,7 +742,7 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
result->valid = 0; result->valid = 0;
result->error_code = NOSTR_ERROR_EVENT_INVALID_KIND; result->error_code = NOSTR_ERROR_EVENT_INVALID_KIND;
snprintf(result->reason, sizeof(result->reason), snprintf(result->reason, sizeof(result->reason),
"Unsupported event kind %d for authentication. Use kind 22242 for NIP-42 or kind 24242 for Blossom.", "Unsupported event kind %d for authentication. Use kind 22242 for NIP-42, kind 24242 for Blossom, or kind 33335 for Admin.",
event_kind); event_kind);
cJSON_Delete(event); cJSON_Delete(event);
return NOSTR_SUCCESS; return NOSTR_SUCCESS;
@@ -1423,6 +1497,118 @@ static int validate_nip42_event(cJSON *event, const char *relay_url,
return NOSTR_SUCCESS; return NOSTR_SUCCESS;
} }
/**
* Validate Admin/Configuration event (kind 33335)
*/
static int validate_admin_event(cJSON *event, const char *method, const char *endpoint) {
if (!event) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Check event kind (must be 33335 for Admin operations)
cJSON *kind_json = cJSON_GetObjectItem(event, "kind");
if (!kind_json || !cJSON_IsNumber(kind_json)) {
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
int kind = cJSON_GetNumberValue(kind_json);
if (kind != 33335) {
return NOSTR_ERROR_EVENT_INVALID_KIND;
}
// Get pubkey for admin authorization check
cJSON *pubkey_json = cJSON_GetObjectItem(event, "pubkey");
if (!pubkey_json || !cJSON_IsString(pubkey_json)) {
return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
}
const char *event_pubkey = cJSON_GetStringValue(pubkey_json);
if (!event_pubkey || strlen(event_pubkey) != 64) {
return NOSTR_ERROR_EVENT_INVALID_PUBKEY;
}
// Check if pubkey is authorized for admin operations
if (strlen(g_auth_cache.admin_pubkey) == 64) {
if (strcmp(event_pubkey, g_auth_cache.admin_pubkey) != 0) {
validator_debug_log("VALIDATOR_DEBUG: Admin pubkey mismatch - access denied\n");
return NOSTR_ERROR_AUTH_REQUIRED;
}
} else {
validator_debug_log("VALIDATOR_DEBUG: No admin pubkey configured - access denied\n");
return NOSTR_ERROR_AUTH_REQUIRED;
}
// Validate admin event tag structure
cJSON *tags = cJSON_GetObjectItem(event, "tags");
if (!tags || !cJSON_IsArray(tags)) {
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// Track what we find in the event tags
int has_method_tag = 0;
int has_endpoint_tag = 0;
int method_matches = (method == NULL); // If no expected method, consider it matched
int endpoint_matches = (endpoint == NULL); // If no expected endpoint, consider it matched
time_t expiration = 0;
cJSON *tag = NULL;
cJSON_ArrayForEach(tag, tags) {
if (!cJSON_IsArray(tag))
continue;
cJSON *tag_name = cJSON_GetArrayItem(tag, 0);
if (!tag_name || !cJSON_IsString(tag_name))
continue;
const char *tag_name_str = cJSON_GetStringValue(tag_name);
if (strcmp(tag_name_str, "method") == 0) {
has_method_tag = 1;
cJSON *method_value = cJSON_GetArrayItem(tag, 1);
if (method_value && cJSON_IsString(method_value)) {
const char *event_method = cJSON_GetStringValue(method_value);
if (method && strcmp(event_method, method) == 0) {
method_matches = 1;
}
}
} else if (strcmp(tag_name_str, "endpoint") == 0) {
has_endpoint_tag = 1;
cJSON *endpoint_value = cJSON_GetArrayItem(tag, 1);
if (endpoint_value && cJSON_IsString(endpoint_value)) {
const char *event_endpoint = cJSON_GetStringValue(endpoint_value);
// For endpoint matching, allow prefix matching for API endpoints
if (endpoint && strncmp(event_endpoint, endpoint, strlen(endpoint)) == 0) {
endpoint_matches = 1;
}
}
} else if (strcmp(tag_name_str, "expiration") == 0) {
cJSON *exp_value = cJSON_GetArrayItem(tag, 1);
if (exp_value && cJSON_IsString(exp_value)) {
expiration = (time_t)atol(cJSON_GetStringValue(exp_value));
}
}
}
// Admin events should have method and endpoint tags
if (!has_method_tag || !has_endpoint_tag) {
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// If we have expected values, they must match
if (!method_matches || !endpoint_matches) {
return NOSTR_ERROR_EVENT_INVALID_TAGS;
}
// Check expiration
time_t now = time(NULL);
if (expiration > 0 && now > expiration) {
return NOSTR_ERROR_EVENT_EXPIRED;
}
validator_debug_log("VALIDATOR_DEBUG: Admin event validation passed\n");
return NOSTR_SUCCESS;
}
//============================================================================= //=============================================================================
// NIP-42 CHALLENGE MANAGEMENT FUNCTIONS // NIP-42 CHALLENGE MANAGEMENT FUNCTIONS
//============================================================================= //=============================================================================

View File

@@ -83,12 +83,14 @@ generate_admin_keys() {
create_admin_event() { create_admin_event() {
local method="$1" local method="$1"
local endpoint="$2"
local content="admin_request" local content="admin_request"
local expiration=$(($(date +%s) + 3600)) # 1 hour from now local expiration=$(($(date +%s) + 3600)) # 1 hour from now
# Create Nostr event with nak - always use "admin" tag for admin operations # Create Nostr event with nak - use kind 33335 for admin/configuration events
local event=$(nak event -k 24242 -c "$content" \ local event=$(nak event -k 33335 -c "$content" \
--tag t="admin" \ --tag method="$method" \
--tag endpoint="$endpoint" \
--tag expiration="$expiration" \ --tag expiration="$expiration" \
--sec "$ADMIN_PRIVKEY") --sec "$ADMIN_PRIVKEY")
@@ -102,8 +104,8 @@ send_admin_request() {
log_info "Testing $method $endpoint" log_info "Testing $method $endpoint"
# Create authenticated Nostr event # Create authenticated Nostr event with method and endpoint
local event=$(create_admin_event "$method") local event=$(create_admin_event "$method" "$endpoint")
local auth_header="Nostr $(echo "$event" | base64 -w 0)" local auth_header="Nostr $(echo "$event" | base64 -w 0)"
# Send request with curl # Send request with curl