Updates for pocket ScreenResult (including sending 'redacted' with some CLOSED)

This commit is contained in:
Mike Dilger 2025-02-20 12:28:28 +13:00
parent dccf8afcf5
commit db8b29dfc4
No known key found for this signature in database
GPG Key ID: 47581A78D4329BA4
7 changed files with 71 additions and 36 deletions

View File

@ -1,4 +1,5 @@
use chorus::error::{ChorusError, Error};
use pocket_db::ScreenResult;
use pocket_types::{Filter, Id, Pubkey, Tags};
use std::env;
@ -50,7 +51,8 @@ fn main() -> Result<(), Error> {
let mut filter_buffer: [u8; 128] = [0; 128];
let filter =
Filter::from_parts(&[], &[pk], &[], tags, None, None, None, &mut filter_buffer)?;
let events = store.find_events(filter, true, 0, 0, |_| true)?;
let (events, _redacted) =
store.find_events(filter, true, 0, 0, |_| ScreenResult::Match)?;
for event in events.iter() {
store.remove_event(event.id())?;
}

View File

@ -1,4 +1,5 @@
use chorus::error::Error;
use pocket_db::ScreenResult;
use pocket_types::{Event, Filter};
use std::env;
@ -23,9 +24,9 @@ fn main() -> Result<(), Error> {
let mut buffer: [u8; 128] = [0; 128];
let (_incount, _outcount, filter) = Filter::from_json(b"{}", &mut buffer)?;
let screen = |_: &Event| -> bool { true };
let screen = |_: &Event| -> ScreenResult { ScreenResult::Match };
let mut events = store.find_events(
let (mut events, _redacted) = store.find_events(
filter,
config.allow_scraping,
config.allow_scrape_if_limited_to,

View File

@ -1,4 +1,5 @@
use chorus::error::Error;
use pocket_db::ScreenResult;
use pocket_types::{Event, Filter, Kind};
use std::env;
use std::io::Write;
@ -33,9 +34,9 @@ fn main() -> Result<(), Error> {
let mut buffer: [u8; 128] = [0; 128];
let (_incount, _outcount, filter) = Filter::from_json(b"{}", &mut buffer)?;
let screen = |_: &Event| -> bool { true };
let screen = |_: &Event| -> ScreenResult { ScreenResult::Match };
let mut events = store.find_events(
let (mut events, _redacted) = store.find_events(
filter,
config.allow_scraping,
config.allow_scrape_if_limited_to,

View File

@ -27,7 +27,7 @@ use hyper_tungstenite::tungstenite;
use hyper_tungstenite::{HyperWebsocket, WebSocketStream};
use hyper_util::rt::TokioIo;
use neg_storage::NegentropyStorageVector;
use pocket_db::Store;
use pocket_db::{ScreenResult, Store};
use pocket_types::{Id, OwnedFilter, Pubkey};
use speedy::{Readable, Writable};
use std::borrow::Cow;
@ -482,15 +482,21 @@ impl WebSocketService {
'subs: for (subid, filters) in self.subscriptions.iter() {
for filter in filters.iter() {
if filter.event_matches(event)?
&& nostr::screen_outgoing_event(event, &event_flags, authorized_user)
{
let message = NostrReply::Event(subid, event);
// note, this is not currently counted in throttling
self.websocket
.send(Message::text(message.as_json()))
.await?;
continue 'subs;
if filter.event_matches(event)? {
let screen_result =
nostr::screen_outgoing_event(event, &event_flags, authorized_user);
if screen_result == ScreenResult::Redacted {
// TBD: Update subscription so the final close can
// let them know there were redactions from
// the post-EOSE data
} else if screen_result == ScreenResult::Match {
let message = NostrReply::Event(subid, event);
// note, this is not currently counted in throttling
self.websocket
.send(Message::text(message.as_json()))
.await?;
continue 'subs;
}
}
}
}

View File

@ -5,6 +5,7 @@ use crate::reply::{NostrReply, NostrReplyPrefix};
use crate::WebSocketService;
use hyper_tungstenite::tungstenite::Message;
use negentropy::{Bytes, Negentropy};
use pocket_db::ScreenResult;
use pocket_types::json::{eat_whitespace, json_unescape, verify_char};
use pocket_types::{read_hex, Event, Filter, Hll8, Kind, OwnedFilter, Pubkey, Time};
use url::Url;
@ -148,6 +149,8 @@ impl WebSocketService {
let completes = filters.iter().all(|f| f.completes());
let mut redacted: bool = false;
// NOTE on private events (DMs, GiftWraps)
// As seen above, we will send CLOSED auth-required if they ask for DMs and are not
// AUTHed yet.
@ -159,11 +162,11 @@ impl WebSocketService {
let mut events: Vec<&Event> = Vec::new();
for filter in filters.iter() {
let screen = |event: &Event| {
let screen = |event: &Event| -> ScreenResult {
let event_flags = event_flags(event, &user);
screen_outgoing_event(event, &event_flags, authorized_user)
};
let filter_events = {
let (filter_events, was_redacted) = {
let config = &*GLOBALS.config.read();
GLOBALS.store.get().unwrap().find_events(
filter,
@ -174,6 +177,7 @@ impl WebSocketService {
)?
};
events.extend(filter_events);
redacted = redacted || was_redacted;
}
// sort
@ -204,7 +208,15 @@ impl WebSocketService {
if completes {
// Closed
let reply = NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned());
let reply = if redacted {
NostrReply::Closed(subid, NostrReplyPrefix::None, "".to_owned())
} else {
NostrReply::Closed(
subid,
NostrReplyPrefix::Redacted,
"Some matching events could not be served to you.".to_owned(),
)
};
self.send(Message::text(reply.as_json())).await?;
} else {
// EOSE
@ -531,11 +543,11 @@ impl WebSocketService {
// Find all matching events
let mut events: Vec<&Event> = Vec::new();
let screen = |event: &Event| {
let screen = |event: &Event| -> ScreenResult {
let event_flags = event_flags(event, &user);
screen_outgoing_event(event, &event_flags, authorized_user)
};
let filter_events = {
let (filter_events, _redacted) = {
let config = &*GLOBALS.config.read();
GLOBALS.store.get().unwrap().find_events(
&filter,
@ -775,59 +787,63 @@ pub fn screen_outgoing_event(
event: &Event,
event_flags: &EventFlags,
authorized_user: bool,
) -> bool {
) -> ScreenResult {
// Forbid if it is a private event (DM or GiftWrap) and theey are neither the recipient
// nor the author
if event.kind() == Kind::from(4) || event.kind() == Kind::from(1059) {
return event_flags.tags_current_user || event_flags.author_is_current_user;
if event_flags.tags_current_user || event_flags.author_is_current_user {
return ScreenResult::Match;
} else {
return ScreenResult::Redacted;
}
}
// Forbid (and delete) if it has an expired expiration tag
if matches!(event.is_expired(), Ok(true)) {
let _ = GLOBALS.store.get().unwrap().remove_event(event.id());
return false;
return ScreenResult::Mismatch;
}
// Allow if an open relay
if GLOBALS.config.read().open_relay {
return true;
return ScreenResult::Match;
}
// Allow Relay Lists
if GLOBALS.config.read().serve_relay_lists
&& (event.kind() == Kind::from(10002) || event.kind() == Kind::from(10050))
{
return true;
return ScreenResult::Match;
}
// Allow if event kind ephemeral
if event.kind().is_ephemeral() && GLOBALS.config.read().serve_ephemeral {
return true;
return ScreenResult::Match;
}
// Allow if an authorized_user is asking
if authorized_user {
return true;
return ScreenResult::Match;
}
// Everybody can see events from our authorized users
if event_flags.author_is_an_authorized_user {
return true;
return ScreenResult::Match;
}
// Allow if event is explicitly approved
if let Ok(Some(true)) = crate::get_event_approval(GLOBALS.store.get().unwrap(), event.id()) {
return true;
return ScreenResult::Match;
}
// Allow if author is explicitly approved
if let Ok(Some(true)) = crate::get_pubkey_approval(GLOBALS.store.get().unwrap(), event.pubkey())
{
return true;
return ScreenResult::Match;
}
// Do not allow the rest
false
ScreenResult::Redacted
}
pub struct EventFlags {

View File

@ -9,6 +9,7 @@ pub enum NostrReplyPrefix {
Duplicate,
Blocked,
RateLimited,
Redacted,
Restricted,
Invalid,
Error,
@ -23,6 +24,7 @@ impl fmt::Display for NostrReplyPrefix {
NostrReplyPrefix::Duplicate => write!(f, "duplicate: "),
NostrReplyPrefix::Blocked => write!(f, "blocked: "),
NostrReplyPrefix::RateLimited => write!(f, "rate-limited: "),
NostrReplyPrefix::Redacted => write!(f, "redacted: "),
NostrReplyPrefix::Restricted => write!(f, "restricted: "),
NostrReplyPrefix::Invalid => write!(f, "invalid: "),
NostrReplyPrefix::Error => write!(f, "error: "),

View File

@ -5,6 +5,7 @@ use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt, Full};
use hyper::body::{Bytes, Incoming};
use hyper::{Request, Response, StatusCode};
use pocket_db::ScreenResult;
use pocket_types::{Event, Filter, Id, Kind, Pubkey};
use serde::Serialize;
use serde_json::{json, Map, Value};
@ -340,15 +341,21 @@ pub fn handle_inner(pubkey: Pubkey, command: Value) -> Result<Option<Value>, Err
let (_incount, _outcount, filter) = Filter::from_json(b"{}", &mut buffer)?;
filter
};
let screen = |e: &Event| -> bool {
!allowed_kinds.contains(&e.kind())
&& !e.kind().is_ephemeral()
&& !crate::is_authorized_user(e.pubkey())
let screen = |e: &Event| -> ScreenResult {
if allowed_kinds.contains(&e.kind()) {
ScreenResult::Mismatch
} else if e.kind().is_ephemeral() {
ScreenResult::Mismatch
} else if crate::is_authorized_user(e.pubkey()) {
ScreenResult::Mismatch
} else {
ScreenResult::Match
}
};
let mut need_moderation: Vec<EventNeedingModeration> = Vec::new();
let mut events = GLOBALS
let (mut events, _redacted) = GLOBALS
.store
.get()
.unwrap()