Finished NIP42 implementation
This commit is contained in:
parent
f7b74bd22c
commit
d06206bb24
|
@ -50,6 +50,8 @@
|
|||
env = {
|
||||
DATABASE_URL = "/tmp/sqlite.db";
|
||||
ADMIN_PUBKEY = "npub1m2w0ckmkgj4wtvl8muwjynh56j3qd4nddca4exdg4mdrkepvfnhsmusy54";
|
||||
CONFIG_ENABLE_AUTH = "false";
|
||||
CONFIG_RELAY_URL = "ws://0.0.0.0:8080";
|
||||
RUST_BACKTRACE = 1;
|
||||
RUST_LOG = "debug";
|
||||
};
|
||||
|
|
|
@ -41,6 +41,10 @@ in {
|
|||
Local nixos-container ip address
|
||||
'';
|
||||
};
|
||||
relayUrl = mkOption {
|
||||
type = types.str;
|
||||
description = "Relay URL that will be used for NIP-42 AUTH validation";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
@ -70,6 +74,7 @@ in {
|
|||
DATABASE_URL = "${DB_PATH}/sneedstr.db";
|
||||
ADMIN_PUBKEY = cfg.adminPubkey;
|
||||
CONFIG_ENABLE_AUTH = cfg.enableAuth;
|
||||
CONFIG_RELAY_URL = cfg.relayUrl;
|
||||
};
|
||||
startLimitBurst = 1;
|
||||
startLimitIntervalSec = 10;
|
||||
|
@ -117,6 +122,10 @@ in {
|
|||
proxyWebsockets = true; # needed if you need to use WebSocket
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
locations."/register" = {
|
||||
proxyPass = "http://${cfg.localAddress}:8085";
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ use tokio::sync::{broadcast, Mutex};
|
|||
pub mod channels {
|
||||
pub static MSG_NOOSE: &str = "MSG_NOOSE";
|
||||
pub static MSG_NIP05: &str = "MSG_NIP05";
|
||||
pub static MSG_AUTH: &str = "MSG_AUTH";
|
||||
pub static MSG_RELAY: &str = "MSG_RELAY";
|
||||
pub static MSG_PIPELINE: &str = "MSG_PIPELINE";
|
||||
pub static MSG_SLED: &str = "MSG_SLED";
|
||||
|
@ -111,7 +112,7 @@ impl PubSub {
|
|||
}
|
||||
|
||||
pub async fn subscribe(&self, topic: &str) -> broadcast::Receiver<Message> {
|
||||
let (tx, _rx) = broadcast::channel(32); // 32 is the channel capacity
|
||||
let (tx, _rx) = broadcast::channel(256); // 256 is the channel capacity
|
||||
let mut subscribers = self.subscribers.lock().await;
|
||||
subscribers
|
||||
.entry(topic.to_string())
|
||||
|
|
|
@ -22,7 +22,7 @@ impl Pipeline {
|
|||
let channel;
|
||||
let command = match message.content {
|
||||
Command::PipelineReqEvent(client_id, event) => {
|
||||
match self.handle_event(client_id, event.clone()).await {
|
||||
match self.handle_event(client_id, event).await {
|
||||
Ok(_) => {
|
||||
log::info!("[Pipeline] handle_event completed");
|
||||
channel = message.source;
|
||||
|
|
|
@ -1496,7 +1496,6 @@ impl Noose for NostrSqlite {
|
|||
}
|
||||
|
||||
async fn write_event(&self, event: Box<nostr::Event>) -> Result<nostr::RelayMessage, Error> {
|
||||
// TODO: Maybe do event validation and admin deletions here
|
||||
match self.save_event(&event).await {
|
||||
Ok(status) => {
|
||||
let relay_message = nostr::RelayMessage::ok(event.id, status, "");
|
||||
|
|
|
@ -215,36 +215,60 @@ fn send(client: &Client, message: Message) {
|
|||
}
|
||||
}
|
||||
|
||||
fn needs_auth(context: &Context, client: &Client, event: Option<&Event>) -> bool {
|
||||
if !context.config.auth_required() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if event.is_some() {
|
||||
let event = event.unwrap();
|
||||
let admin_pk = context.config.get_admin_pubkey();
|
||||
if event.pubkey == *admin_pk {
|
||||
return false;
|
||||
}
|
||||
|
||||
if event.kind() == nostr::Kind::Metadata {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !client.authenticated {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
async fn handle_msg(context: &Context, client: &mut Client, client_message: ClientMessage) {
|
||||
match client_message {
|
||||
ClientMessage::Event(event) => {
|
||||
if context.config.auth_required()
|
||||
&& event.kind() != nostr::Kind::Metadata
|
||||
&& !client.authenticated
|
||||
{
|
||||
if needs_auth(context, client, Some(&event)) {
|
||||
request_auth(context, client).await;
|
||||
return;
|
||||
}
|
||||
|
||||
handle_event(context, client, event).await
|
||||
}
|
||||
ClientMessage::Req {
|
||||
subscription_id,
|
||||
filters,
|
||||
} => {
|
||||
if context.config.auth_required() && !client.authenticated {
|
||||
if needs_auth(context, client, None) {
|
||||
request_auth(context, client).await;
|
||||
return;
|
||||
}
|
||||
|
||||
handle_req(context, client, subscription_id, filters).await
|
||||
}
|
||||
ClientMessage::Count {
|
||||
subscription_id,
|
||||
filters,
|
||||
} => {
|
||||
if context.config.auth_required() && !client.authenticated {
|
||||
if needs_auth(context, client, None) {
|
||||
request_auth(context, client).await;
|
||||
return;
|
||||
}
|
||||
|
||||
handle_count(context, client, subscription_id, filters).await
|
||||
}
|
||||
ClientMessage::Close(subscription_id) => handle_close(client, subscription_id).await,
|
||||
|
@ -378,9 +402,51 @@ async fn handle_close(client: &mut Client, subscription_id: SubscriptionId) {
|
|||
}
|
||||
|
||||
async fn handle_auth(context: &Context, client: &mut Client, event: Box<Event>) {
|
||||
client.authenticate(&event);
|
||||
let mut subscriber = context.pubsub.subscribe(channels::MSG_AUTH).await;
|
||||
context
|
||||
.pubsub
|
||||
.publish(
|
||||
channels::MSG_NOOSE,
|
||||
crate::bussy::Message {
|
||||
source: channels::MSG_AUTH,
|
||||
content: crate::bussy::Command::DbReqGetProfile(event.pubkey),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut message = nostr::RelayMessage::ok(
|
||||
event.id,
|
||||
client.authenticated,
|
||||
"auth-required: User not registered",
|
||||
);
|
||||
|
||||
let Ok(result) = subscriber.recv().await else {
|
||||
context
|
||||
.pubsub
|
||||
.publish(
|
||||
channels::MSG_RELAY,
|
||||
crate::bussy::Message {
|
||||
source: channels::MSG_RELAY,
|
||||
content: crate::bussy::Command::DbResOkWithStatus(client.client_id, message),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
};
|
||||
|
||||
if let crate::bussy::Command::DbResGetProfile(profile) = result.content {
|
||||
client.authenticate(context.config.get_relay_url(), &event);
|
||||
|
||||
let client_status = format!("Client authenticated: {}", client.authenticated);
|
||||
let message = nostr::RelayMessage::notice(client_status);
|
||||
let status_message = if client.authenticated {
|
||||
""
|
||||
} else {
|
||||
"auth-required: we only accept events from registered users"
|
||||
};
|
||||
|
||||
message = nostr::RelayMessage::ok(event.id, client.authenticated, status_message);
|
||||
};
|
||||
|
||||
context
|
||||
.pubsub
|
||||
.publish(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use nostr::{key::FromPkStr, secp256k1::XOnlyPublicKey};
|
||||
|
||||
|
@ -7,6 +7,7 @@ pub struct Config {
|
|||
admin_pubkey: XOnlyPublicKey,
|
||||
db_path: PathBuf,
|
||||
auth_required: bool,
|
||||
relay_url: nostr::UncheckedUrl,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -28,11 +29,13 @@ impl Config {
|
|||
.unwrap_or("false".to_string())
|
||||
.parse()
|
||||
.unwrap();
|
||||
let relay_url = nostr::UncheckedUrl::from_str(&std::env::var("CONFIG_RELAY_URL").unwrap()).unwrap();
|
||||
|
||||
Self {
|
||||
admin_pubkey,
|
||||
db_path,
|
||||
auth_required,
|
||||
relay_url,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,12 +51,16 @@ impl Config {
|
|||
self.db_path.clone()
|
||||
}
|
||||
|
||||
pub fn get_relay_url(&self) -> nostr::UncheckedUrl {
|
||||
self.relay_url.clone()
|
||||
}
|
||||
|
||||
pub fn get_relay_config_json(&self) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"contact": "klink@zhitno.st",
|
||||
"name": "zhitno.st",
|
||||
"description": "Very *special* nostr relay",
|
||||
"supported_nips": [ 1, 2, 9, 11, 12, 15, 16, 20, 22, 28, 33, 40, 42, 45, 50 ],
|
||||
"supported_nips": [ 1, 2, 4, 9, 11, 12, 15, 16, 20, 22, 28, 33, 40, 42, 45, 50 ],
|
||||
"software": "git+https://git.zhitno.st/Klink/sneedstr.git",
|
||||
"version": "0.1.1"
|
||||
})
|
||||
|
|
|
@ -77,13 +77,13 @@ impl Client {
|
|||
self.challlenge = Some(challenge);
|
||||
}
|
||||
|
||||
pub fn authenticate(&mut self, event: &nostr::Event) {
|
||||
pub fn authenticate(&mut self, relay_url: nostr::UncheckedUrl, event: &nostr::Event) {
|
||||
if self.challlenge.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let challenge_tag = nostr::Tag::Challenge(self.challlenge.clone().unwrap());
|
||||
let relay_tag = nostr::Tag::Relay(nostr::UncheckedUrl::from_str("ws://0.0.0.0:8080").unwrap()); // TODO: Use relay address from env variable
|
||||
let relay_tag = nostr::Tag::Relay(relay_url);
|
||||
if let Ok(()) = event.verify() {
|
||||
log::debug!("event is valid");
|
||||
if event.kind.as_u32() == 22242 && event.created_at() > nostr::Timestamp::from(nostr::Timestamp::now().as_u64() - 600) {
|
||||
|
|
Loading…
Reference in a new issue