feat: send notices when authorization checks fail

This commit is contained in:
Greg Heartsfield 2022-02-13 09:35:54 -06:00
parent 77f35f9f43
commit 5913b9f87a
3 changed files with 45 additions and 17 deletions

View File

@ -80,6 +80,7 @@ reject_future_seconds = 1800
# metadata for event authors, "passive" to perform validation but # metadata for event authors, "passive" to perform validation but
# never block publishing, or "disabled" to do nothing. # never block publishing, or "disabled" to do nothing.
#mode = "disabled" #mode = "disabled"
mode = "passive"
# Domain names that will be prevented from publishing events. # Domain names that will be prevented from publishing events.
#domain_blacklist = ["wellorder.net"] #domain_blacklist = ["wellorder.net"]

View File

@ -27,6 +27,13 @@ use tokio::task;
pub type SqlitePool = r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>; pub type SqlitePool = r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>;
pub type PooledConnection = r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>; pub type PooledConnection = r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
/// Events submitted from a client, with a return channel for notices
pub struct SubmittedEvent {
pub event: Event,
pub notice_tx: tokio::sync::mpsc::Sender<String>,
}
/// Database file /// Database file
pub const DB_FILE: &str = "nostr.db"; pub const DB_FILE: &str = "nostr.db";
@ -76,7 +83,7 @@ pub fn build_conn(flags: OpenFlags) -> Result<Connection> {
/// Spawn a database writer that persists events to the SQLite store. /// Spawn a database writer that persists events to the SQLite store.
pub async fn db_writer( pub async fn db_writer(
mut event_rx: tokio::sync::mpsc::Receiver<Event>, mut event_rx: tokio::sync::mpsc::Receiver<SubmittedEvent>,
bcast_tx: tokio::sync::broadcast::Sender<Event>, bcast_tx: tokio::sync::broadcast::Sender<Event>,
metadata_tx: tokio::sync::broadcast::Sender<Event>, metadata_tx: tokio::sync::broadcast::Sender<Event>,
mut shutdown: tokio::sync::broadcast::Receiver<()>, mut shutdown: tokio::sync::broadcast::Receiver<()>,
@ -131,18 +138,20 @@ pub async fn db_writer(
break; break;
} }
let mut event_write = false; let mut event_write = false;
let event = next_event.unwrap(); let subm_event = next_event.unwrap();
let event = subm_event.event;
let notice_tx = subm_event.notice_tx;
// check if this event is authorized. // check if this event is authorized.
if let Some(allowed_addrs) = whitelist { if let Some(allowed_addrs) = whitelist {
debug!("Checking against pubkey whitelist");
// if the event address is not in allowed_addrs. // if the event address is not in allowed_addrs.
if !allowed_addrs.contains(&event.pubkey) { if !allowed_addrs.contains(&event.pubkey) {
info!( info!(
"Rejecting event {}, unauthorized author", "Rejecting event {}, unauthorized author",
event.get_event_id_prefix() event.get_event_id_prefix()
); );
// TODO: define a channel that can send NOTICEs back to the client. notice_tx
.try_send("pubkey is not allowed to publish to this relay".to_owned())
.ok();
continue; continue;
} }
} }
@ -171,6 +180,12 @@ pub async fn db_writer(
uv.name.to_string(), uv.name.to_string(),
event.get_author_prefix() event.get_author_prefix()
); );
notice_tx
.try_send(
"NIP-05 verification is no longer valid (expired/wrong domain)"
.to_owned(),
)
.ok();
continue; continue;
} }
} }
@ -179,6 +194,9 @@ pub async fn db_writer(
"no verification records found for pubkey: {:?}", "no verification records found for pubkey: {:?}",
event.get_author_prefix() event.get_author_prefix()
); );
notice_tx
.try_send("NIP-05 verification needed to publish events".to_owned())
.ok();
continue; continue;
} }
Err(e) => { Err(e) => {
@ -207,6 +225,12 @@ pub async fn db_writer(
} }
Err(err) => { Err(err) => {
warn!("event insert failed: {:?}", err); warn!("event insert failed: {:?}", err);
notice_tx
.try_send(
"relay experienced an error trying to publish the latest event"
.to_owned(),
)
.ok();
} }
} }

View File

@ -13,6 +13,7 @@ use nostr_rs_relay::close::CloseCmd;
use nostr_rs_relay::config; use nostr_rs_relay::config;
use nostr_rs_relay::conn; use nostr_rs_relay::conn;
use nostr_rs_relay::db; use nostr_rs_relay::db;
use nostr_rs_relay::db::SubmittedEvent;
use nostr_rs_relay::error::{Error, Result}; use nostr_rs_relay::error::{Error, Result};
use nostr_rs_relay::event::Event; use nostr_rs_relay::event::Event;
use nostr_rs_relay::event::EventCmd; use nostr_rs_relay::event::EventCmd;
@ -51,7 +52,7 @@ async fn handle_web_request(
pool: db::SqlitePool, pool: db::SqlitePool,
remote_addr: SocketAddr, remote_addr: SocketAddr,
broadcast: Sender<Event>, broadcast: Sender<Event>,
event_tx: tokio::sync::mpsc::Sender<Event>, event_tx: tokio::sync::mpsc::Sender<SubmittedEvent>,
shutdown: Receiver<()>, shutdown: Receiver<()>,
) -> Result<Response<Body>, Infallible> { ) -> Result<Response<Body>, Infallible> {
match ( match (
@ -232,7 +233,8 @@ fn main() -> Result<(), Error> {
let (bcast_tx, _) = broadcast::channel::<Event>(settings.limits.broadcast_buffer); let (bcast_tx, _) = broadcast::channel::<Event>(settings.limits.broadcast_buffer);
// validated events that need to be persisted are sent to the // validated events that need to be persisted are sent to the
// database on via this channel. // database on via this channel.
let (event_tx, event_rx) = mpsc::channel::<Event>(settings.limits.event_persist_buffer); let (event_tx, event_rx) =
mpsc::channel::<SubmittedEvent>(settings.limits.event_persist_buffer);
// establish a channel for letting all threads now about a // establish a channel for letting all threads now about a
// requested server shutdown. // requested server shutdown.
let (invoke_shutdown, shutdown_listen) = broadcast::channel::<()>(1); let (invoke_shutdown, shutdown_listen) = broadcast::channel::<()>(1);
@ -359,7 +361,7 @@ async fn nostr_server(
pool: db::SqlitePool, pool: db::SqlitePool,
mut ws_stream: WebSocketStream<Upgraded>, mut ws_stream: WebSocketStream<Upgraded>,
broadcast: Sender<Event>, broadcast: Sender<Event>,
event_tx: tokio::sync::mpsc::Sender<Event>, event_tx: mpsc::Sender<SubmittedEvent>,
mut shutdown: Receiver<()>, mut shutdown: Receiver<()>,
) { ) {
// get a broadcast channel for clients to communicate on // get a broadcast channel for clients to communicate on
@ -370,6 +372,9 @@ async fn nostr_server(
// Create a channel for receiving query results from the database. // Create a channel for receiving query results from the database.
// we will send out the tx handle to any query we generate. // we will send out the tx handle to any query we generate.
let (query_tx, mut query_rx) = mpsc::channel::<db::QueryResult>(256); let (query_tx, mut query_rx) = mpsc::channel::<db::QueryResult>(256);
// Create channel for receiving NOTICEs
let (notice_tx, mut notice_rx) = mpsc::channel::<String>(32);
// maintain a hashmap of a oneshot channel for active subscriptions. // maintain a hashmap of a oneshot channel for active subscriptions.
// when these subscriptions are cancelled, make a message // when these subscriptions are cancelled, make a message
// available to the executing query so it knows to stop. // available to the executing query so it knows to stop.
@ -408,9 +413,12 @@ async fn nostr_server(
// Send a ping // Send a ping
ws_stream.send(Message::Ping(Vec::new())).await.ok(); ws_stream.send(Message::Ping(Vec::new())).await.ok();
}, },
Some(notice_msg) = notice_rx.recv() => {
let n = notice_msg.to_string().replace("\"", "");
ws_stream.send(Message::Text(format!("[\"NOTICE\",\"{}\"]", n))).await.ok();
},
Some(query_result) = query_rx.recv() => { Some(query_result) = query_rx.recv() => {
// database informed us of a query result we asked for // database informed us of a query result we asked for
//let res = EventRes(query_result.sub_id,query_result.event);
client_received_event_count += 1; client_received_event_count += 1;
// send a result // send a result
let subesc = query_result.sub_id.replace("\"", ""); let subesc = query_result.sub_id.replace("\"", "");
@ -474,14 +482,9 @@ async fn nostr_server(
Ok(e) => { Ok(e) => {
let id_prefix:String = e.id.chars().take(8).collect(); let id_prefix:String = e.id.chars().take(8).collect();
debug!("successfully parsed/validated event: {:?} from client: {:?}", id_prefix, cid); debug!("successfully parsed/validated event: {:?} from client: {:?}", id_prefix, cid);
// TODO: consider moving some/all // Write this to the database.
// authorization checks here, instead let submit_event = SubmittedEvent { event: e.clone(), notice_tx: notice_tx.clone() };
// of the DB module, so we can send a event_tx.send(submit_event).await.ok();
// proper NOTICE back to the client if
// they are unable to write.
// Write this to the database
event_tx.send(e.clone()).await.ok();
client_published_event_count += 1; client_published_event_count += 1;
}, },
Err(_) => { Err(_) => {