diff --git a/src/apub.rs b/src/apub.rs index df8e411..7cff20a 100644 --- a/src/apub.rs +++ b/src/apub.rs @@ -115,13 +115,20 @@ impl ValidObjects { } } - pub fn child_object_is_actor(&self) -> bool { + pub fn is(&self, uri: &XsdAnyUri) -> bool { + match self { + ValidObjects::Id(id) => id == uri, + ValidObjects::Object(AnyExistingObject { id, .. }) => id == uri, + } + } + + pub fn child_object_is(&self, uri: &XsdAnyUri) -> bool { match self { ValidObjects::Id(_) => false, ValidObjects::Object(AnyExistingObject { ext, .. }) => { if let Some(o) = ext.get("object") { - if let Ok(s) = serde_json::from_value::(o.clone()) { - return s.ends_with("/actor"); + if let Ok(child_uri) = serde_json::from_value::(o.clone()) { + return child_uri == *uri; } } diff --git a/src/error.rs b/src/error.rs index 80adda6..9f8bb5a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,8 +30,17 @@ pub enum MyError { #[error("Couldn't decode base64")] Base64(#[from] base64::DecodeError), - #[error("Actor tried to submit another actor's payload")] - BadActor, + #[error("Actor is blocked, {0}")] + Blocked(String), + + #[error("Actor is not whitelisted, {0}")] + Whitelist(String), + + #[error("Cannot make decisions for foreign actor, {0}")] + WrongActor(String), + + #[error("Actor ({0}) tried to submit another actor's ({1}) payload")] + BadActor(String, String), #[error("Invalid algorithm provided to verifier")] Algorithm, @@ -42,12 +51,6 @@ pub enum MyError { #[error("Object has already been relayed")] Duplicate, - #[error("Actor is blocked")] - Blocked, - - #[error("Actor is not whitelisted")] - Whitelist, - #[error("Couldn't send request")] SendRequest, diff --git a/src/inbox.rs b/src/inbox.rs index 5fcc7dc..97e14f8 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -34,13 +34,22 @@ pub async fn inbox( ) .await?; + let (is_blocked, is_whitelisted) = + join!(state.is_blocked(&actor.id), state.is_whitelisted(&actor.id),); + + if is_blocked { + return Err(MyError::Blocked(actor.id.to_string())); + } + + if !is_whitelisted { + return Err(MyError::Whitelist(actor.id.to_string())); + } + if actor.public_key.id.as_str() != verified.key_id() { - error!( - "Request payload and requestor disagree on actor, {} != {}", - input.actor, - verified.key_id() - ); - return Err(MyError::BadActor); + return Err(MyError::BadActor( + actor.public_key.id.to_string(), + verified.key_id().to_owned(), + )); } match input.kind { @@ -66,6 +75,12 @@ async fn handle_undo( return Err(MyError::Kind); } + let my_id: XsdAnyUri = state.generate_url(UrlKind::Actor).parse()?; + + if !input.object.child_object_is(&my_id) { + return Err(MyError::WrongActor(input.object.id().to_string())); + } + let inbox = actor.inbox().to_owned(); db_actor.do_send(DbQuery(move |pool: Pool| { @@ -81,32 +96,18 @@ async fn handle_undo( } })); - let mut undo = Undo::default(); - let mut follow = Follow::default(); - - follow - .object_props - .set_id(state.generate_url(UrlKind::Activity))?; - follow - .follow_props - .set_actor_xsd_any_uri(actor.id.clone())? - .set_object_xsd_any_uri(actor.id.clone())?; - - undo.object_props - .set_id(state.generate_url(UrlKind::Activity))? - .set_many_to_xsd_any_uris(vec![actor.id.clone()])? - .set_context_xsd_any_uri(context())?; - undo.undo_props - .set_object_object_box(follow)? - .set_actor_xsd_any_uri(state.generate_url(UrlKind::Actor))?; - - if input.object.child_object_is_actor() { - let undo2 = undo.clone(); - let client = client.into_inner(); - actix::Arbiter::spawn(async move { - let _ = deliver(&state.into_inner(), &client, actor.id, &undo2).await; - }); - } + let actor_inbox = actor.inbox().clone(); + let undo = generate_undo_follow(&state, &actor.id, &my_id)?; + let undo2 = undo.clone(); + actix::Arbiter::spawn(async move { + let _ = deliver( + &state.into_inner(), + &client.into_inner(), + actor_inbox, + &undo2, + ) + .await; + }); Ok(response(undo)) } @@ -120,8 +121,7 @@ async fn handle_forward( let object_id = input.object.id(); let inboxes = get_inboxes(&state, &actor, &object_id).await?; - - deliver_many(state, client, inboxes, input.clone()); + deliver_many(&state, &client, inboxes, input.clone()); Ok(response(input)) } @@ -140,24 +140,12 @@ async fn handle_relay( let activity_id: XsdAnyUri = state.generate_url(UrlKind::Activity).parse()?; - let mut announce = Announce::default(); - announce - .object_props - .set_context_xsd_any_uri(context())? - .set_many_to_xsd_any_uris(vec![state.generate_url(UrlKind::Followers)])? - .set_id(activity_id.clone())?; - - announce - .announce_props - .set_object_xsd_any_uri(object_id.clone())? - .set_actor_xsd_any_uri(state.generate_url(UrlKind::Actor))?; - + let announce = generate_announce(&state, &activity_id, object_id)?; let inboxes = get_inboxes(&state, &actor, &object_id).await?; + deliver_many(&state, &client, inboxes, announce.clone()); state.cache(object_id.to_owned(), activity_id).await; - deliver_many(state, client, inboxes, announce.clone()); - Ok(response(announce)) } @@ -168,21 +156,13 @@ async fn handle_follow( input: AcceptedObjects, actor: AcceptedActors, ) -> Result { - let (is_listener, is_blocked, is_whitelisted) = join!( - state.is_listener(&actor.id), - state.is_blocked(&actor.id), - state.is_whitelisted(&actor.id) - ); + let my_id: XsdAnyUri = state.generate_url(UrlKind::Actor).parse()?; - if is_blocked { - error!("Follow from blocked listener, {}", actor.id); - return Err(MyError::Blocked); + if !input.object.is(&my_id) { + return Err(MyError::WrongActor(input.object.id().to_string())); } - if !is_whitelisted { - error!("Follow from non-whitelisted listener, {}", actor.id); - return Err(MyError::Whitelist); - } + let is_listener = state.is_listener(&actor.id).await; if !is_listener { let inbox = actor.inbox().to_owned(); @@ -198,34 +178,136 @@ async fn handle_follow( }) } })); + + let actor_inbox = actor.inbox().clone(); + let follow = generate_follow(&state, &actor.id, &my_id)?; + let state2 = state.clone(); + let client2 = client.clone(); + actix::Arbiter::spawn(async move { + let _ = deliver( + &state2.into_inner(), + &client2.into_inner(), + actor_inbox, + &follow, + ) + .await; + }); } let actor_inbox = actor.inbox().clone(); + let accept = generate_accept_follow(&state, &actor.id, &input.id, &my_id)?; + let accept2 = accept.clone(); + actix::Arbiter::spawn(async move { + let _ = deliver( + &state.into_inner(), + &client.into_inner(), + actor_inbox, + &accept2, + ) + .await; + }); - let mut accept = Accept::default(); + Ok(response(accept)) +} + +// Generate a type that says "I want to stop following you" +fn generate_undo_follow( + state: &web::Data, + actor_id: &XsdAnyUri, + my_id: &XsdAnyUri, +) -> Result { + let mut undo = Undo::default(); let mut follow = Follow::default(); - follow.object_props.set_id(input.id)?; + + follow + .object_props + .set_id(state.generate_url(UrlKind::Activity))?; follow .follow_props - .set_object_xsd_any_uri(state.generate_url(UrlKind::Actor))? - .set_actor_xsd_any_uri(actor.id.clone())?; + .set_actor_xsd_any_uri(actor_id.clone())? + .set_object_xsd_any_uri(actor_id.clone())?; + + undo.object_props + .set_id(state.generate_url(UrlKind::Activity))? + .set_many_to_xsd_any_uris(vec![actor_id.clone()])? + .set_context_xsd_any_uri(context())?; + undo.undo_props + .set_object_object_box(follow)? + .set_actor_xsd_any_uri(my_id.clone())?; + + Ok(undo) +} + +// Generate a type that says "Look at this object" +fn generate_announce( + state: &web::Data, + activity_id: &XsdAnyUri, + object_id: &XsdAnyUri, +) -> Result { + let mut announce = Announce::default(); + + announce + .object_props + .set_context_xsd_any_uri(context())? + .set_many_to_xsd_any_uris(vec![state.generate_url(UrlKind::Followers)])? + .set_id(activity_id.clone())?; + + announce + .announce_props + .set_object_xsd_any_uri(object_id.clone())? + .set_actor_xsd_any_uri(state.generate_url(UrlKind::Actor))?; + + Ok(announce) +} + +// Generate a type that says "I want to follow you" +fn generate_follow( + state: &web::Data, + actor_id: &XsdAnyUri, + my_id: &XsdAnyUri, +) -> Result { + let mut follow = Follow::default(); + + follow + .object_props + .set_id(state.generate_url(UrlKind::Activity))? + .set_many_to_xsd_any_uris(vec![actor_id.clone()])? + .set_context_xsd_any_uri(context())?; + + follow + .follow_props + .set_object_xsd_any_uri(actor_id.clone())? + .set_actor_xsd_any_uri(my_id.clone())?; + + Ok(follow) +} + +// Generate a type that says "I accept your follow request" +fn generate_accept_follow( + state: &web::Data, + actor_id: &XsdAnyUri, + input_id: &XsdAnyUri, + my_id: &XsdAnyUri, +) -> Result { + let mut accept = Accept::default(); + let mut follow = Follow::default(); + + follow.object_props.set_id(input_id.clone())?; + follow + .follow_props + .set_object_xsd_any_uri(my_id.clone())? + .set_actor_xsd_any_uri(actor_id.clone())?; accept .object_props .set_id(state.generate_url(UrlKind::Activity))? - .set_many_to_xsd_any_uris(vec![actor.id])?; + .set_many_to_xsd_any_uris(vec![actor_id.clone()])?; accept .accept_props .set_object_object_box(follow)? - .set_actor_xsd_any_uri(state.generate_url(UrlKind::Actor))?; + .set_actor_xsd_any_uri(my_id.clone())?; - let client = client.into_inner(); - let accept2 = accept.clone(); - actix::Arbiter::spawn(async move { - let _ = deliver(&state.into_inner(), &client.clone(), actor_inbox, &accept2).await; - }); - - Ok(response(accept)) + Ok(accept) } async fn get_inboxes( diff --git a/src/requests.rs b/src/requests.rs index 9d15d3d..f69fd84 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -57,15 +57,15 @@ pub async fn fetch_actor( } pub fn deliver_many( - state: web::Data, - client: web::Data, + state: &web::Data, + client: &web::Data, inboxes: Vec, item: T, ) where T: serde::ser::Serialize + 'static, { - let client = client.into_inner(); - let state = state.into_inner(); + let client = client.clone().into_inner(); + let state = state.clone().into_inner(); actix::Arbiter::spawn(async move { use futures::stream::StreamExt;