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

View File

@@ -113,6 +113,7 @@ static int validate_blossom_event(cJSON *event, const char *expected_hash,
const char *method);
static int validate_nip42_event(cJSON *event, const char *relay_url,
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,
const char *resource_hash);
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");
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 {
char unsupported_msg[256];
sprintf(unsupported_msg,
@@ -668,7 +742,7 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
result->valid = 0;
result->error_code = NOSTR_ERROR_EVENT_INVALID_KIND;
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);
cJSON_Delete(event);
return NOSTR_SUCCESS;
@@ -1423,6 +1497,118 @@ static int validate_nip42_event(cJSON *event, const char *relay_url,
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
//=============================================================================