feat(NIP-42): limit access to kind 4 DMs

This commit is contained in:
rorp 2023-02-25 17:57:04 -08:00 committed by Greg Heartsfield
parent c13961a5c4
commit 8e4e2d824b
3 changed files with 43 additions and 10 deletions

View File

@ -148,6 +148,8 @@ reject_future_seconds = 1800
#] #]
# Enable NIP-42 authentication # Enable NIP-42 authentication
#nip42_auth = false #nip42_auth = false
# Send DMs events (kind 4) only to their authenticated recipients
#nip42_dms = false
[verified_users] [verified_users]
# NIP-05 verification of users. Can be "enabled" to require NIP-05 # NIP-05 verification of users. Can be "enabled" to require NIP-05

View File

@ -80,6 +80,7 @@ pub struct Limits {
pub struct Authorization { pub struct Authorization {
pub pubkey_whitelist: Option<Vec<String>>, // If present, only allow these pubkeys to publish events pub pubkey_whitelist: Option<Vec<String>>, // If present, only allow these pubkeys to publish events
pub nip42_auth: bool, // if true enables NIP-42 authentication pub nip42_auth: bool, // if true enables NIP-42 authentication
pub nip42_dms: bool, // if true send DMs only to their authenticated recipients
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -285,6 +286,7 @@ impl Default for Settings {
authorization: Authorization { authorization: Authorization {
pubkey_whitelist: None, // Allow any address to publish pubkey_whitelist: None, // Allow any address to publish
nip42_auth: false, // Disable NIP-42 authentication nip42_auth: false, // Disable NIP-42 authentication
nip42_dms: false, // Send DMs to everybody
}, },
pay_to_relay: PayToRelay { pay_to_relay: PayToRelay {
enabled: false, enabled: false,

View File

@ -1029,6 +1029,31 @@ fn make_notice_message(notice: &Notice) -> Message {
Message::text(json.to_string()) Message::text(json.to_string())
} }
fn allowed_to_send(event_str: &String, conn: &conn::ClientConn, settings: &Settings) -> bool {
// TODO: pass in kind so that we can avoid deserialization for most events
if settings.authorization.nip42_dms {
match serde_json::from_str::<Event>(event_str) {
Ok(event) => {
if event.kind == 4 {
match (conn.auth_pubkey(), event.tag_values_by_name("p").first()) {
(Some(auth_pubkey), Some(recipient_pubkey)) => {
recipient_pubkey == auth_pubkey || &event.pubkey == auth_pubkey
},
(_, _) => {
false
},
}
} else {
true
}
},
Err(_) => false
}
} else {
true
}
}
struct ClientInfo { struct ClientInfo {
remote_ip: String, remote_ip: String,
user_agent: Option<String>, user_agent: Option<String>,
@ -1151,12 +1176,14 @@ async fn nostr_server(
let send_str = format!("[\"EOSE\",\"{subesc}\"]"); let send_str = format!("[\"EOSE\",\"{subesc}\"]");
ws_stream.send(Message::Text(send_str)).await.ok(); ws_stream.send(Message::Text(send_str)).await.ok();
} else { } else {
client_received_event_count += 1; if allowed_to_send(&query_result.event, &conn, &settings) {
metrics.sent_events.with_label_values(&["db"]).inc(); metrics.sent_events.with_label_values(&["db"]).inc();
client_received_event_count += 1;
// send a result // send a result
let send_str = format!("[\"EVENT\",\"{}\",{}]", subesc, &query_result.event); let send_str = format!("[\"EVENT\",\"{}\",{}]", subesc, &query_result.event);
ws_stream.send(Message::Text(send_str)).await.ok(); ws_stream.send(Message::Text(send_str)).await.ok();
} }
}
}, },
// TODO: consider logging the LaggedRecv error // TODO: consider logging the LaggedRecv error
Ok(global_event) = bcast_rx.recv() => { Ok(global_event) = bcast_rx.recv() => {
@ -1169,13 +1196,15 @@ async fn nostr_server(
// TODO: serialize at broadcast time, instead of // TODO: serialize at broadcast time, instead of
// once for each consumer. // once for each consumer.
if let Ok(event_str) = serde_json::to_string(&global_event) { if let Ok(event_str) = serde_json::to_string(&global_event) {
if allowed_to_send(&event_str, &conn, &settings) {
// create an event response and send it
trace!("sub match for client: {}, sub: {:?}, event: {:?}", trace!("sub match for client: {}, sub: {:?}, event: {:?}",
cid, s, cid, s,
global_event.get_event_id_prefix()); global_event.get_event_id_prefix());
// create an event response and send it
let subesc = s.replace('"', ""); let subesc = s.replace('"', "");
metrics.sent_events.with_label_values(&["realtime"]).inc(); metrics.sent_events.with_label_values(&["realtime"]).inc();
ws_stream.send(Message::Text(format!("[\"EVENT\",\"{subesc}\",{event_str}]"))).await.ok(); ws_stream.send(Message::Text(format!("[\"EVENT\",\"{subesc}\",{event_str}]"))).await.ok();
}
} else { } else {
warn!("could not serialize event: {:?}", global_event.get_event_id_prefix()); warn!("could not serialize event: {:?}", global_event.get_event_id_prefix());
} }