Bring whitelist/block checks to outermost level, fix deliveries

This commit is contained in:
asonix 2020-03-17 21:16:09 -05:00
parent e849df2ef4
commit a4ec70d6ec
4 changed files with 180 additions and 88 deletions

View file

@ -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 { match self {
ValidObjects::Id(_) => false, ValidObjects::Id(_) => false,
ValidObjects::Object(AnyExistingObject { ext, .. }) => { ValidObjects::Object(AnyExistingObject { ext, .. }) => {
if let Some(o) = ext.get("object") { if let Some(o) = ext.get("object") {
if let Ok(s) = serde_json::from_value::<String>(o.clone()) { if let Ok(child_uri) = serde_json::from_value::<XsdAnyUri>(o.clone()) {
return s.ends_with("/actor"); return child_uri == *uri;
} }
} }

View file

@ -30,8 +30,17 @@ pub enum MyError {
#[error("Couldn't decode base64")] #[error("Couldn't decode base64")]
Base64(#[from] base64::DecodeError), Base64(#[from] base64::DecodeError),
#[error("Actor tried to submit another actor's payload")] #[error("Actor is blocked, {0}")]
BadActor, 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")] #[error("Invalid algorithm provided to verifier")]
Algorithm, Algorithm,
@ -42,12 +51,6 @@ pub enum MyError {
#[error("Object has already been relayed")] #[error("Object has already been relayed")]
Duplicate, Duplicate,
#[error("Actor is blocked")]
Blocked,
#[error("Actor is not whitelisted")]
Whitelist,
#[error("Couldn't send request")] #[error("Couldn't send request")]
SendRequest, SendRequest,

View file

@ -34,13 +34,22 @@ pub async fn inbox(
) )
.await?; .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() { if actor.public_key.id.as_str() != verified.key_id() {
error!( return Err(MyError::BadActor(
"Request payload and requestor disagree on actor, {} != {}", actor.public_key.id.to_string(),
input.actor, verified.key_id().to_owned(),
verified.key_id() ));
);
return Err(MyError::BadActor);
} }
match input.kind { match input.kind {
@ -66,6 +75,12 @@ async fn handle_undo(
return Err(MyError::Kind); 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(); let inbox = actor.inbox().to_owned();
db_actor.do_send(DbQuery(move |pool: Pool| { db_actor.do_send(DbQuery(move |pool: Pool| {
@ -81,32 +96,18 @@ async fn handle_undo(
} }
})); }));
let mut undo = Undo::default(); let actor_inbox = actor.inbox().clone();
let mut follow = Follow::default(); let undo = generate_undo_follow(&state, &actor.id, &my_id)?;
let undo2 = undo.clone();
follow actix::Arbiter::spawn(async move {
.object_props let _ = deliver(
.set_id(state.generate_url(UrlKind::Activity))?; &state.into_inner(),
follow &client.into_inner(),
.follow_props actor_inbox,
.set_actor_xsd_any_uri(actor.id.clone())? &undo2,
.set_object_xsd_any_uri(actor.id.clone())?; )
.await;
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;
});
}
Ok(response(undo)) Ok(response(undo))
} }
@ -120,8 +121,7 @@ async fn handle_forward(
let object_id = input.object.id(); let object_id = input.object.id();
let inboxes = get_inboxes(&state, &actor, &object_id).await?; 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)) Ok(response(input))
} }
@ -140,24 +140,12 @@ async fn handle_relay(
let activity_id: XsdAnyUri = state.generate_url(UrlKind::Activity).parse()?; let activity_id: XsdAnyUri = state.generate_url(UrlKind::Activity).parse()?;
let mut announce = Announce::default(); let announce = generate_announce(&state, &activity_id, object_id)?;
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 inboxes = get_inboxes(&state, &actor, &object_id).await?; 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; state.cache(object_id.to_owned(), activity_id).await;
deliver_many(state, client, inboxes, announce.clone());
Ok(response(announce)) Ok(response(announce))
} }
@ -168,21 +156,13 @@ async fn handle_follow(
input: AcceptedObjects, input: AcceptedObjects,
actor: AcceptedActors, actor: AcceptedActors,
) -> Result<HttpResponse, MyError> { ) -> Result<HttpResponse, MyError> {
let (is_listener, is_blocked, is_whitelisted) = join!( let my_id: XsdAnyUri = state.generate_url(UrlKind::Actor).parse()?;
state.is_listener(&actor.id),
state.is_blocked(&actor.id),
state.is_whitelisted(&actor.id)
);
if is_blocked { if !input.object.is(&my_id) {
error!("Follow from blocked listener, {}", actor.id); return Err(MyError::WrongActor(input.object.id().to_string()));
return Err(MyError::Blocked);
} }
if !is_whitelisted { let is_listener = state.is_listener(&actor.id).await;
error!("Follow from non-whitelisted listener, {}", actor.id);
return Err(MyError::Whitelist);
}
if !is_listener { if !is_listener {
let inbox = actor.inbox().to_owned(); 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 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<State>,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Undo, MyError> {
let mut undo = Undo::default();
let mut follow = Follow::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
.follow_props .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<State>,
activity_id: &XsdAnyUri,
object_id: &XsdAnyUri,
) -> Result<Announce, MyError> {
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<State>,
actor_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Follow, MyError> {
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<State>,
actor_id: &XsdAnyUri,
input_id: &XsdAnyUri,
my_id: &XsdAnyUri,
) -> Result<Accept, MyError> {
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 accept
.object_props .object_props
.set_id(state.generate_url(UrlKind::Activity))? .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
.accept_props .accept_props
.set_object_object_box(follow)? .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(); Ok(accept)
let accept2 = accept.clone();
actix::Arbiter::spawn(async move {
let _ = deliver(&state.into_inner(), &client.clone(), actor_inbox, &accept2).await;
});
Ok(response(accept))
} }
async fn get_inboxes( async fn get_inboxes(

View file

@ -57,15 +57,15 @@ pub async fn fetch_actor(
} }
pub fn deliver_many<T>( pub fn deliver_many<T>(
state: web::Data<State>, state: &web::Data<State>,
client: web::Data<Client>, client: &web::Data<Client>,
inboxes: Vec<XsdAnyUri>, inboxes: Vec<XsdAnyUri>,
item: T, item: T,
) where ) where
T: serde::ser::Serialize + 'static, T: serde::ser::Serialize + 'static,
{ {
let client = client.into_inner(); let client = client.clone().into_inner();
let state = state.into_inner(); let state = state.clone().into_inner();
actix::Arbiter::spawn(async move { actix::Arbiter::spawn(async move {
use futures::stream::StreamExt; use futures::stream::StreamExt;