Bring whitelist/block checks to outermost level, fix deliveries
This commit is contained in:
parent
e849df2ef4
commit
a4ec70d6ec
4 changed files with 180 additions and 88 deletions
13
src/apub.rs
13
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 {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
src/error.rs
19
src/error.rs
|
@ -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,
|
||||||
|
|
||||||
|
|
228
src/inbox.rs
228
src/inbox.rs
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue