v0.7.16 - Fixed blacklist authentication system - removed redundant action/parameters columns, added active=1 filtering, added comprehensive debug tracing, and identified that auth must be enabled for blacklist to work
This commit is contained in:
@@ -218,9 +218,13 @@ button:disabled {
|
|||||||
.config-actions-cell {
|
.config-actions-cell {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
text-align: center;
|
text-align: center !important;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
width: 60px;
|
||||||
|
min-width: 60px;
|
||||||
|
max-width: 60px;
|
||||||
|
padding: 8px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-actions-cell:hover {
|
.config-actions-cell:hover {
|
||||||
|
|||||||
175
api/index.html
175
api/index.html
@@ -86,99 +86,12 @@
|
|||||||
|
|
||||||
</div> <!-- End Main Sections Wrapper -->
|
</div> <!-- End Main Sections Wrapper -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Testing Section -->
|
|
||||||
<div id="div_config" class="section flex-section" style="display: none;">
|
|
||||||
<h2>RELAY CONFIGURATION</h2>
|
|
||||||
<div id="config-display" class="hidden">
|
|
||||||
<div class="config-table-container">
|
|
||||||
<table class="config-table" id="config-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Parameter</th>
|
|
||||||
<th>Value</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="config-table-body">
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline-buttons">
|
|
||||||
<button type="button" id="fetch-config-btn">REFRESH</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Auth Rules Management - Moved after configuration -->
|
|
||||||
<div class="section flex-section" id="authRulesSection" style="display: none;">
|
|
||||||
<div class="section-header">
|
|
||||||
<h2>AUTH RULES MANAGEMENT</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Auth Rules Table -->
|
|
||||||
<div id="authRulesTableContainer" style="display: none;">
|
|
||||||
<table class="config-table" id="authRulesTable">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Rule Type</th>
|
|
||||||
<th>Pattern Type</th>
|
|
||||||
<th>Pattern Value</th>
|
|
||||||
<th>Action</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="authRulesTableBody">
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Simplified Auth Rule Input Section -->
|
|
||||||
<div id="authRuleInputSections" style="display: block;">
|
|
||||||
|
|
||||||
<!-- Combined Pubkey Auth Rule Section -->
|
|
||||||
|
|
||||||
|
|
||||||
<div class="input-group">
|
|
||||||
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
|
|
||||||
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="whitelistWarning" class="warning-box" style="display: none;">
|
|
||||||
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
|
|
||||||
mode.
|
|
||||||
Only whitelisted users will be able to interact with the relay.
|
|
||||||
</div>
|
|
||||||
<div class="inline-buttons">
|
|
||||||
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
|
|
||||||
WHITELIST</button>
|
|
||||||
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
|
|
||||||
BLACKLIST</button>
|
|
||||||
<button type="button" id="refreshAuthRulesBtn">REFRESH</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- DATABASE STATISTICS Section -->
|
<!-- DATABASE STATISTICS Section -->
|
||||||
<div class="section" id="databaseStatisticsSection" style="display: none;">
|
<div class="section flex-section" id="databaseStatisticsSection" style="display: none;">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>DATABASE STATISTICS</h2>
|
<h2>DATABASE STATISTICS</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Database Overview Table -->
|
<!-- Database Overview Table -->
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@@ -300,6 +213,90 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Testing Section -->
|
||||||
|
<div id="div_config" class="section flex-section" style="display: none;">
|
||||||
|
<h2>RELAY CONFIGURATION</h2>
|
||||||
|
<div id="config-display" class="hidden">
|
||||||
|
<div class="config-table-container">
|
||||||
|
<table class="config-table" id="config-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Parameter</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="config-table-body">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="inline-buttons">
|
||||||
|
<button type="button" id="fetch-config-btn">REFRESH</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auth Rules Management - Moved after configuration -->
|
||||||
|
<div class="section flex-section" id="authRulesSection" style="display: none;">
|
||||||
|
<div class="section-header">
|
||||||
|
<h2>AUTH RULES MANAGEMENT</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auth Rules Table -->
|
||||||
|
<div id="authRulesTableContainer" style="display: none;">
|
||||||
|
<table class="config-table" id="authRulesTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Rule Type</th>
|
||||||
|
<th>Pattern Type</th>
|
||||||
|
<th>Pattern Value</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="authRulesTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Simplified Auth Rule Input Section -->
|
||||||
|
<div id="authRuleInputSections" style="display: block;">
|
||||||
|
|
||||||
|
<!-- Combined Pubkey Auth Rule Section -->
|
||||||
|
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
|
||||||
|
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="whitelistWarning" class="warning-box" style="display: none;">
|
||||||
|
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
|
||||||
|
mode.
|
||||||
|
Only whitelisted users will be able to interact with the relay.
|
||||||
|
</div>
|
||||||
|
<div class="inline-buttons">
|
||||||
|
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
|
||||||
|
WHITELIST</button>
|
||||||
|
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
|
||||||
|
BLACKLIST</button>
|
||||||
|
<button type="button" id="refreshAuthRulesBtn">REFRESH</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- NIP-17 DIRECT MESSAGES Section -->
|
<!-- NIP-17 DIRECT MESSAGES Section -->
|
||||||
<div class="section" id="nip17DMSection" style="display: none;">
|
<div class="section" id="nip17DMSection" style="display: none;">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
|
|||||||
79
api/index.js
79
api/index.js
@@ -808,7 +808,7 @@
|
|||||||
|
|
||||||
// Add to inbox
|
// Add to inbox
|
||||||
const timestamp = new Date(event.created_at * 1000).toLocaleString();
|
const timestamp = new Date(event.created_at * 1000).toLocaleString();
|
||||||
addMessageToInbox('received', decryptedContent, timestamp);
|
addMessageToInbox('received', decryptedContent, timestamp, event.pubkey);
|
||||||
|
|
||||||
// Log for testing
|
// Log for testing
|
||||||
if (typeof logTestEvent === 'function') {
|
if (typeof logTestEvent === 'function') {
|
||||||
@@ -845,7 +845,7 @@
|
|||||||
|
|
||||||
// Add to inbox
|
// Add to inbox
|
||||||
const timestamp = new Date(event.created_at * 1000).toLocaleString();
|
const timestamp = new Date(event.created_at * 1000).toLocaleString();
|
||||||
addMessageToInbox('received', rumor.content, timestamp);
|
addMessageToInbox('received', rumor.content, timestamp, rumor.pubkey);
|
||||||
|
|
||||||
// Log for testing
|
// Log for testing
|
||||||
if (typeof logTestEvent === 'function') {
|
if (typeof logTestEvent === 'function') {
|
||||||
@@ -1848,7 +1848,6 @@
|
|||||||
<td>${rule.rule_type}</td>
|
<td>${rule.rule_type}</td>
|
||||||
<td>${rule.pattern_type || rule.operation || '-'}</td>
|
<td>${rule.pattern_type || rule.operation || '-'}</td>
|
||||||
<td style="font-family: 'Courier New', monospace; font-size: 12px; word-break: break-all; max-width: 200px;">${rule.pattern_value || rule.rule_target || '-'}</td>
|
<td style="font-family: 'Courier New', monospace; font-size: 12px; word-break: break-all; max-width: 200px;">${rule.pattern_value || rule.rule_target || '-'}</td>
|
||||||
<td>${rule.action || 'allow'}</td>
|
|
||||||
<td>${rule.enabled !== false ? 'Active' : 'Inactive'}</td>
|
<td>${rule.enabled !== false ? 'Active' : 'Inactive'}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="inline-buttons">
|
<div class="inline-buttons">
|
||||||
@@ -2159,7 +2158,7 @@
|
|||||||
// STREAMLINED AUTH RULE FUNCTIONS
|
// STREAMLINED AUTH RULE FUNCTIONS
|
||||||
// ================================
|
// ================================
|
||||||
|
|
||||||
// Utility function to convert nsec to hex pubkey
|
// Utility function to convert nsec to hex pubkey or npub to hex pubkey
|
||||||
function nsecToHex(input) {
|
function nsecToHex(input) {
|
||||||
if (!input || input.trim().length === 0) {
|
if (!input || input.trim().length === 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -2178,11 +2177,17 @@
|
|||||||
if (window.NostrTools && window.NostrTools.nip19 && window.NostrTools.nip19.decode) {
|
if (window.NostrTools && window.NostrTools.nip19 && window.NostrTools.nip19.decode) {
|
||||||
const decoded = window.NostrTools.nip19.decode(trimmed);
|
const decoded = window.NostrTools.nip19.decode(trimmed);
|
||||||
if (decoded.type === 'nsec') {
|
if (decoded.type === 'nsec') {
|
||||||
// Convert bytes to hex
|
// Handle different versions of nostr-tools
|
||||||
const hexPubkey = Array.from(decoded.data)
|
if (typeof decoded.data === 'string') {
|
||||||
.map(b => b.toString(16).padStart(2, '0'))
|
// v1 style - data is already hex
|
||||||
.join('');
|
return decoded.data;
|
||||||
return hexPubkey;
|
} else {
|
||||||
|
// v2 style - data is Uint8Array
|
||||||
|
const hexPubkey = Array.from(decoded.data)
|
||||||
|
.map(b => b.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
return hexPubkey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2191,6 +2196,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it starts with npub1, try to decode to hex
|
||||||
|
if (trimmed.startsWith('npub1')) {
|
||||||
|
try {
|
||||||
|
if (window.NostrTools && window.NostrTools.nip19 && window.NostrTools.nip19.decode) {
|
||||||
|
const decoded = window.NostrTools.nip19.decode(trimmed);
|
||||||
|
if (decoded.type === 'npub') {
|
||||||
|
// Handle different versions of nostr-tools
|
||||||
|
if (typeof decoded.data === 'string') {
|
||||||
|
// v1 style - data is already hex
|
||||||
|
return decoded.data;
|
||||||
|
} else {
|
||||||
|
// v2 style - data is Uint8Array
|
||||||
|
const hexPubkey = Array.from(decoded.data)
|
||||||
|
.map(b => b.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
return hexPubkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to decode npub:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null; // Invalid format
|
return null; // Invalid format
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2206,10 +2236,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert nsec to hex if needed
|
// Convert nsec or npub to hex if needed
|
||||||
const hexPubkey = nsecToHex(inputValue);
|
const hexPubkey = nsecToHex(inputValue);
|
||||||
if (!hexPubkey) {
|
if (!hexPubkey) {
|
||||||
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
log('Invalid pubkey format. Please enter nsec1..., npub1..., or 64-character hex', 'ERROR');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2258,10 +2288,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert nsec to hex if needed
|
// Convert nsec or npub to hex if needed
|
||||||
const hexPubkey = nsecToHex(inputValue);
|
const hexPubkey = nsecToHex(inputValue);
|
||||||
if (!hexPubkey) {
|
if (!hexPubkey) {
|
||||||
log('Invalid pubkey format. Please enter nsec1... or 64-character hex', 'ERROR');
|
log('Invalid pubkey format. Please enter nsec1..., npub1..., or 64-character hex', 'ERROR');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3065,21 +3095,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add message to inbox display
|
// Add message to inbox display
|
||||||
function addMessageToInbox(direction, message, timestamp) {
|
function addMessageToInbox(direction, message, timestamp, pubkey = null) {
|
||||||
if (!dmInbox) return;
|
if (!dmInbox) return;
|
||||||
|
|
||||||
const messageDiv = document.createElement('div');
|
const messageDiv = document.createElement('div');
|
||||||
messageDiv.className = 'log-entry';
|
messageDiv.className = 'log-entry';
|
||||||
|
|
||||||
const directionColor = direction === 'sent' ? '#007bff' : '#28a745';
|
const directionColor = direction === 'sent' ? '#007bff' : '#28a745';
|
||||||
|
|
||||||
// Convert newlines to <br> tags for proper HTML display
|
// Convert newlines to <br> tags for proper HTML display
|
||||||
const formattedMessage = message.replace(/\n/g, '<br>');
|
const formattedMessage = message.replace(/\n/g, '<br>');
|
||||||
|
|
||||||
|
// Add pubkey display for received messages
|
||||||
|
let pubkeyDisplay = '';
|
||||||
|
if (pubkey && direction === 'received') {
|
||||||
|
try {
|
||||||
|
const npub = window.NostrTools.nip19.npubEncode(pubkey);
|
||||||
|
pubkeyDisplay = ` <span style="color: #666; font-size: 11px;">(${npub})</span>`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to encode pubkey to npub:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
messageDiv.innerHTML = `
|
messageDiv.innerHTML = `
|
||||||
<span class="log-timestamp">${timestamp}</span>
|
<span class="log-timestamp">${timestamp}</span>
|
||||||
<span style="color: ${directionColor}; font-weight: bold;">[${direction.toUpperCase()}]</span>
|
<span style="color: ${directionColor}; font-weight: bold;">[${direction.toUpperCase()}]</span>
|
||||||
<span style="white-space: pre-wrap;">${formattedMessage}</span>
|
<span style="white-space: pre-wrap;">${formattedMessage}${pubkeyDisplay}</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Remove the "No messages received yet" placeholder if it exists
|
// Remove the "No messages received yet" placeholder if it exists
|
||||||
@@ -3399,10 +3440,10 @@
|
|||||||
|
|
||||||
data.top_pubkeys.forEach((pubkey, index) => {
|
data.top_pubkeys.forEach((pubkey, index) => {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
const shortPubkey = pubkey.pubkey ? pubkey.pubkey.substring(0, 16) + '...' : '-';
|
const npub = pubkey.pubkey ? window.NostrTools.nip19.npubEncode(pubkey.pubkey) : '-';
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>${index + 1}</td>
|
<td>${index + 1}</td>
|
||||||
<td style="font-family: 'Courier New', monospace; font-size: 12px;">${shortPubkey}</td>
|
<td style="font-family: 'Courier New', monospace; font-size: 12px; word-break: break-all;">${npub}</td>
|
||||||
<td>${pubkey.event_count}</td>
|
<td>${pubkey.event_count}</td>
|
||||||
<td>${pubkey.percentage}%</td>
|
<td>${pubkey.percentage}%</td>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -37,4 +37,8 @@ You're all set up now - just wait for the next crash and then run the coredumpct
|
|||||||
Even simpler: Use this one-liner
|
Even simpler: Use this one-liner
|
||||||
# Start relay and immediately attach gdb
|
# Start relay and immediately attach gdb
|
||||||
cd /usr/local/bin/c_relay
|
cd /usr/local/bin/c_relay
|
||||||
sudo -u c-relay ./c_relay --debug-level=5 & sleep 2 && sudo gdb -p $(pgrep c_relay)
|
sudo -u c-relay ./c_relay --debug-level=5 & sleep 2 && sudo gdb -p $(pgrep c_relay)
|
||||||
|
|
||||||
|
Once gdb attaches, type continue and wait for the crash. This way the relay starts normally and gdb just monitors it.
|
||||||
|
|
||||||
|
Which approach would you like to try?
|
||||||
36
src/config.c
36
src/config.c
@@ -64,7 +64,7 @@ int process_admin_config_event(cJSON* event, char* error_message, size_t error_s
|
|||||||
// Forward declaration for relay info initialization
|
// Forward declaration for relay info initialization
|
||||||
void init_relay_info(void);
|
void init_relay_info(void);
|
||||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||||
const char* pattern_value, const char* action);
|
const char* pattern_value);
|
||||||
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||||
const char* pattern_value);
|
const char* pattern_value);
|
||||||
int is_config_table_ready(void);
|
int is_config_table_ready(void);
|
||||||
@@ -2067,28 +2067,27 @@ int process_admin_auth_event(cJSON* event, char* error_message, size_t error_siz
|
|||||||
|
|
||||||
// Add auth rule from configuration
|
// Add auth rule from configuration
|
||||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||||
const char* pattern_value, const char* action) {
|
const char* pattern_value) {
|
||||||
if (!g_db || !rule_type || !pattern_type || !pattern_value || !action) {
|
if (!g_db || !rule_type || !pattern_type || !pattern_value) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* sql = "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value, action) "
|
const char* sql = "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value) "
|
||||||
"VALUES (?, ?, ?, ?)";
|
"VALUES (?, ?, ?)";
|
||||||
|
|
||||||
sqlite3_stmt* stmt;
|
sqlite3_stmt* stmt;
|
||||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_STATIC);
|
||||||
sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_STATIC);
|
||||||
sqlite3_bind_text(stmt, 3, pattern_value, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 3, pattern_value, -1, SQLITE_STATIC);
|
||||||
sqlite3_bind_text(stmt, 4, action, -1, SQLITE_STATIC);
|
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
return (rc == SQLITE_DONE) ? 0 : -1;
|
return (rc == SQLITE_DONE) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2725,13 +2724,13 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
|||||||
|
|
||||||
// Build appropriate SQL query based on query type
|
// Build appropriate SQL query based on query type
|
||||||
if (strcmp(query_type, "all") == 0) {
|
if (strcmp(query_type, "all") == 0) {
|
||||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules ORDER BY rule_type, pattern_type";
|
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE active = 1 ORDER BY rule_type, pattern_type";
|
||||||
}
|
}
|
||||||
else if (strcmp(query_type, "whitelist") == 0) {
|
else if (strcmp(query_type, "whitelist") == 0) {
|
||||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules WHERE rule_type LIKE '%whitelist%' ORDER BY pattern_type";
|
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE rule_type LIKE '%whitelist%' AND active = 1 ORDER BY pattern_type";
|
||||||
}
|
}
|
||||||
else if (strcmp(query_type, "blacklist") == 0) {
|
else if (strcmp(query_type, "blacklist") == 0) {
|
||||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules WHERE rule_type LIKE '%blacklist%' ORDER BY pattern_type";
|
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE rule_type LIKE '%blacklist%' AND active = 1 ORDER BY pattern_type";
|
||||||
}
|
}
|
||||||
else if (strcmp(query_type, "pattern") == 0) {
|
else if (strcmp(query_type, "pattern") == 0) {
|
||||||
// Get pattern value from tags
|
// Get pattern value from tags
|
||||||
@@ -2740,7 +2739,7 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
|||||||
snprintf(error_message, error_size, "invalid: pattern query requires pattern value");
|
snprintf(error_message, error_size, "invalid: pattern query requires pattern value");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sql = "SELECT rule_type, pattern_type, pattern_value, action FROM auth_rules WHERE pattern_value = ? ORDER BY rule_type, pattern_type";
|
sql = "SELECT rule_type, pattern_type, pattern_value FROM auth_rules WHERE pattern_value = ? AND active = 1 ORDER BY rule_type, pattern_type";
|
||||||
use_pattern_param = 1;
|
use_pattern_param = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2775,7 +2774,6 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
|||||||
const char* rule_type = (const char*)sqlite3_column_text(stmt, 0);
|
const char* rule_type = (const char*)sqlite3_column_text(stmt, 0);
|
||||||
const char* pattern_type = (const char*)sqlite3_column_text(stmt, 1);
|
const char* pattern_type = (const char*)sqlite3_column_text(stmt, 1);
|
||||||
const char* pattern_value_result = (const char*)sqlite3_column_text(stmt, 2);
|
const char* pattern_value_result = (const char*)sqlite3_column_text(stmt, 2);
|
||||||
const char* action = (const char*)sqlite3_column_text(stmt, 3);
|
|
||||||
|
|
||||||
// printf(" %s %s:%s -> %s\n",
|
// printf(" %s %s:%s -> %s\n",
|
||||||
// rule_type ? rule_type : "",
|
// rule_type ? rule_type : "",
|
||||||
@@ -2788,7 +2786,7 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
|
|||||||
cJSON_AddStringToObject(rule_obj, "rule_type", rule_type ? rule_type : "");
|
cJSON_AddStringToObject(rule_obj, "rule_type", rule_type ? rule_type : "");
|
||||||
cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type ? pattern_type : "");
|
cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type ? pattern_type : "");
|
||||||
cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value_result ? pattern_value_result : "");
|
cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value_result ? pattern_value_result : "");
|
||||||
cJSON_AddStringToObject(rule_obj, "action", action ? action : "allow");
|
cJSON_AddStringToObject(rule_obj, "action", "allow"); // Simplified: rule_type determines behavior
|
||||||
cJSON_AddItemToArray(results_array, rule_obj);
|
cJSON_AddItemToArray(results_array, rule_obj);
|
||||||
|
|
||||||
rule_count++;
|
rule_count++;
|
||||||
@@ -3314,7 +3312,7 @@ int handle_auth_rule_modification_unified(cJSON* event, char* error_message, siz
|
|||||||
|
|
||||||
// Process auth rule: ["blacklist"|"whitelist", "pubkey"|"hash", "value"]
|
// Process auth rule: ["blacklist"|"whitelist", "pubkey"|"hash", "value"]
|
||||||
if (strcmp(rule_type, "blacklist") == 0 || strcmp(rule_type, "whitelist") == 0) {
|
if (strcmp(rule_type, "blacklist") == 0 || strcmp(rule_type, "whitelist") == 0) {
|
||||||
if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value, "allow") == 0) {
|
if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value) == 0) {
|
||||||
rules_processed++;
|
rules_processed++;
|
||||||
|
|
||||||
// Add processed rule to response array
|
// Add processed rule to response array
|
||||||
@@ -3322,7 +3320,7 @@ int handle_auth_rule_modification_unified(cJSON* event, char* error_message, siz
|
|||||||
cJSON_AddStringToObject(rule_obj, "rule_type", rule_type);
|
cJSON_AddStringToObject(rule_obj, "rule_type", rule_type);
|
||||||
cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type);
|
cJSON_AddStringToObject(rule_obj, "pattern_type", pattern_type);
|
||||||
cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value);
|
cJSON_AddStringToObject(rule_obj, "pattern_value", pattern_value);
|
||||||
cJSON_AddStringToObject(rule_obj, "action", "allow");
|
cJSON_AddStringToObject(rule_obj, "action", "allow"); // Simplified: rule_type determines behavior
|
||||||
cJSON_AddStringToObject(rule_obj, "status", "added");
|
cJSON_AddStringToObject(rule_obj, "status", "added");
|
||||||
cJSON_AddItemToArray(processed_rules, rule_obj);
|
cJSON_AddItemToArray(processed_rules, rule_obj);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ cJSON* build_query_response(const char* query_type, cJSON* results_array, int to
|
|||||||
|
|
||||||
// Auth rules management functions
|
// Auth rules management functions
|
||||||
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
int add_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||||
const char* pattern_value, const char* action);
|
const char* pattern_value);
|
||||||
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
int remove_auth_rule_from_config(const char* rule_type, const char* pattern_type,
|
||||||
const char* pattern_value);
|
const char* pattern_value);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,6 +15,7 @@
|
|||||||
#include "../nostr_core_lib/nostr_core/nip013.h" // NIP-13: Proof of Work
|
#include "../nostr_core_lib/nostr_core/nip013.h" // NIP-13: Proof of Work
|
||||||
#include "../nostr_core_lib/nostr_core/nostr_common.h"
|
#include "../nostr_core_lib/nostr_core/nostr_common.h"
|
||||||
#include "../nostr_core_lib/nostr_core/utils.h"
|
#include "../nostr_core_lib/nostr_core/utils.h"
|
||||||
|
#include "debug.h" // C-relay debug system
|
||||||
#include "config.h" // C-relay configuration system
|
#include "config.h" // C-relay configuration system
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -531,6 +532,8 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
|||||||
sqlite3_stmt *stmt = NULL;
|
sqlite3_stmt *stmt = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
DEBUG_TRACE("Checking auth rules for pubkey: %s", pubkey);
|
||||||
|
|
||||||
if (!pubkey) {
|
if (!pubkey) {
|
||||||
return NOSTR_ERROR_INVALID_INPUT;
|
return NOSTR_ERROR_INVALID_INPUT;
|
||||||
}
|
}
|
||||||
@@ -547,19 +550,21 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
|||||||
|
|
||||||
// Step 1: Check pubkey blacklist (highest priority)
|
// Step 1: Check pubkey blacklist (highest priority)
|
||||||
const char *blacklist_sql =
|
const char *blacklist_sql =
|
||||||
"SELECT rule_type, action FROM auth_rules WHERE rule_type = "
|
"SELECT rule_type FROM auth_rules WHERE rule_type = "
|
||||||
"'blacklist' AND pattern_type = 'pubkey' AND pattern_value = ? LIMIT 1";
|
"'blacklist' AND pattern_type = 'pubkey' AND pattern_value = ? AND active = 1 LIMIT 1";
|
||||||
|
DEBUG_TRACE("Blacklist SQL: %s", blacklist_sql);
|
||||||
rc = sqlite3_prepare_v2(db, blacklist_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, blacklist_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
int step_result = sqlite3_step(stmt);
|
||||||
const char *action = (const char *)sqlite3_column_text(stmt, 1);
|
DEBUG_TRACE("Blacklist query result: %s", step_result == SQLITE_ROW ? "FOUND" : "NOT_FOUND");
|
||||||
|
|
||||||
|
if (step_result == SQLITE_ROW) {
|
||||||
|
DEBUG_TRACE("BLACKLIST HIT: Denying access for pubkey: %s", pubkey);
|
||||||
// Set specific violation details for status code mapping
|
// Set specific violation details for status code mapping
|
||||||
strcpy(g_last_rule_violation.violation_type, "pubkey_blacklist");
|
strcpy(g_last_rule_violation.violation_type, "pubkey_blacklist");
|
||||||
sprintf(g_last_rule_violation.reason, "Public key blacklisted: %s",
|
sprintf(g_last_rule_violation.reason, "Public key blacklisted");
|
||||||
action ? action : "PUBKEY_BLACKLIST");
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
@@ -571,19 +576,16 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
|||||||
// Step 2: Check hash blacklist
|
// Step 2: Check hash blacklist
|
||||||
if (resource_hash) {
|
if (resource_hash) {
|
||||||
const char *hash_blacklist_sql =
|
const char *hash_blacklist_sql =
|
||||||
"SELECT rule_type, action FROM auth_rules WHERE rule_type = "
|
"SELECT rule_type FROM auth_rules WHERE rule_type = "
|
||||||
"'blacklist' AND pattern_type = 'hash' AND pattern_value = ? LIMIT 1";
|
"'blacklist' AND pattern_type = 'hash' AND pattern_value = ? AND active = 1 LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, hash_blacklist_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, hash_blacklist_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
sqlite3_bind_text(stmt, 1, resource_hash, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, resource_hash, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
const char *action = (const char *)sqlite3_column_text(stmt, 1);
|
|
||||||
|
|
||||||
// Set specific violation details for status code mapping
|
// Set specific violation details for status code mapping
|
||||||
strcpy(g_last_rule_violation.violation_type, "hash_blacklist");
|
strcpy(g_last_rule_violation.violation_type, "hash_blacklist");
|
||||||
sprintf(g_last_rule_violation.reason, "File hash blacklisted: %s",
|
sprintf(g_last_rule_violation.reason, "File hash blacklisted");
|
||||||
action ? action : "HASH_BLACKLIST");
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
@@ -595,8 +597,8 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
|||||||
|
|
||||||
// Step 3: Check pubkey whitelist
|
// Step 3: Check pubkey whitelist
|
||||||
const char *whitelist_sql =
|
const char *whitelist_sql =
|
||||||
"SELECT rule_type, action FROM auth_rules WHERE rule_type = "
|
"SELECT rule_type FROM auth_rules WHERE rule_type = "
|
||||||
"'whitelist' AND pattern_type = 'pubkey' AND pattern_value = ? LIMIT 1";
|
"'whitelist' AND pattern_type = 'pubkey' AND pattern_value = ? AND active = 1 LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, whitelist_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, whitelist_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, pubkey, -1, SQLITE_STATIC);
|
||||||
@@ -612,7 +614,7 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
|
|||||||
// Step 4: Check if any whitelist rules exist - if yes, deny by default
|
// Step 4: Check if any whitelist rules exist - if yes, deny by default
|
||||||
const char *whitelist_exists_sql =
|
const char *whitelist_exists_sql =
|
||||||
"SELECT COUNT(*) FROM auth_rules WHERE rule_type = 'whitelist' "
|
"SELECT COUNT(*) FROM auth_rules WHERE rule_type = 'whitelist' "
|
||||||
"AND pattern_type = 'pubkey' LIMIT 1";
|
"AND pattern_type = 'pubkey' AND active = 1 LIMIT 1";
|
||||||
rc = sqlite3_prepare_v2(db, whitelist_exists_sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, whitelist_exists_sql, -1, &stmt, NULL);
|
||||||
if (rc == SQLITE_OK) {
|
if (rc == SQLITE_OK) {
|
||||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
|||||||
@@ -142,8 +142,6 @@ CREATE TABLE auth_rules (\n\
|
|||||||
rule_type TEXT NOT NULL CHECK (rule_type IN ('whitelist', 'blacklist', 'rate_limit', 'auth_required')),\n\
|
rule_type TEXT NOT NULL CHECK (rule_type IN ('whitelist', 'blacklist', 'rate_limit', 'auth_required')),\n\
|
||||||
pattern_type TEXT NOT NULL CHECK (pattern_type IN ('pubkey', 'kind', 'ip', 'global')),\n\
|
pattern_type TEXT NOT NULL CHECK (pattern_type IN ('pubkey', 'kind', 'ip', 'global')),\n\
|
||||||
pattern_value TEXT,\n\
|
pattern_value TEXT,\n\
|
||||||
action TEXT NOT NULL CHECK (action IN ('allow', 'deny', 'require_auth', 'rate_limit')),\n\
|
|
||||||
parameters TEXT, -- JSON parameters for rate limiting, etc.\n\
|
|
||||||
active INTEGER NOT NULL DEFAULT 1,\n\
|
active INTEGER NOT NULL DEFAULT 1,\n\
|
||||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n\
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),\n\
|
||||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n\
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))\n\
|
||||||
|
|||||||
@@ -516,7 +516,7 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
if (event_kind == 23456) {
|
if (event_kind == 23456) {
|
||||||
if (admin_result != 0) {
|
if (admin_result != 0) {
|
||||||
char error_result_msg[512];
|
char error_result_msg[512];
|
||||||
if (admin_error && strlen(admin_error) > 0) {
|
if (strlen(admin_error) > 0) {
|
||||||
// Safely truncate admin_error if too long
|
// Safely truncate admin_error if too long
|
||||||
size_t max_error_len = sizeof(error_result_msg) - 50; // Leave room for prefix
|
size_t max_error_len = sizeof(error_result_msg) - 50; // Leave room for prefix
|
||||||
size_t error_len = strlen(admin_error);
|
size_t error_len = strlen(admin_error);
|
||||||
@@ -532,7 +532,12 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
"ERROR: Kind %d event processing failed: ", event_kind);
|
"ERROR: Kind %d event processing failed: ", event_kind);
|
||||||
if (prefix_len < sizeof(error_result_msg)) {
|
if (prefix_len < sizeof(error_result_msg)) {
|
||||||
size_t remaining = sizeof(error_result_msg) - prefix_len;
|
size_t remaining = sizeof(error_result_msg) - prefix_len;
|
||||||
strncat(error_result_msg, truncated_error, remaining - 1);
|
size_t copy_len = strlen(truncated_error);
|
||||||
|
if (copy_len >= remaining) {
|
||||||
|
copy_len = remaining - 1;
|
||||||
|
}
|
||||||
|
memcpy(error_result_msg + prefix_len, truncated_error, copy_len);
|
||||||
|
error_result_msg[prefix_len + copy_len] = '\0';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
snprintf(error_result_msg, sizeof(error_result_msg),
|
snprintf(error_result_msg, sizeof(error_result_msg),
|
||||||
|
|||||||
40
tests/post_events.sh
Executable file
40
tests/post_events.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Test script to post kind 1 events to the relay every second
|
||||||
|
# Cycles through three different secret keys
|
||||||
|
# Content includes current timestamp
|
||||||
|
|
||||||
|
# Array of secret keys to cycle through
|
||||||
|
SECRET_KEYS=(
|
||||||
|
"3fdd8227a920c2385559400b2b14e464f22e80df312a73cc7a86e1d7e91d608f"
|
||||||
|
"a156011cd65b71f84b4a488ac81687f2aed57e490b31c28f58195d787030db60"
|
||||||
|
"1618aaa21f5bd45c5ffede0d9a60556db67d4a046900e5f66b0bae5c01c801fb"
|
||||||
|
)
|
||||||
|
|
||||||
|
RELAY_URL="ws://localhost:8888"
|
||||||
|
KEY_INDEX=0
|
||||||
|
|
||||||
|
echo "Starting event posting test to $RELAY_URL"
|
||||||
|
echo "Press Ctrl+C to stop"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
# Get current timestamp
|
||||||
|
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
|
||||||
|
# Get current secret key
|
||||||
|
CURRENT_KEY=${SECRET_KEYS[$KEY_INDEX]}
|
||||||
|
|
||||||
|
# Create content with timestamp
|
||||||
|
CONTENT="Test event at $TIMESTAMP"
|
||||||
|
|
||||||
|
echo "[$TIMESTAMP] Posting event with key ${KEY_INDEX}: ${CURRENT_KEY:0:16}..."
|
||||||
|
|
||||||
|
# Post event using nak
|
||||||
|
nak event -c "$CONTENT" --sec "$CURRENT_KEY" "$RELAY_URL"
|
||||||
|
|
||||||
|
# Cycle to next key
|
||||||
|
KEY_INDEX=$(( (KEY_INDEX + 1) % ${#SECRET_KEYS[@]} ))
|
||||||
|
|
||||||
|
# Wait 1 second
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user