asonix
83b447b780
- Fix image federation - Enable submission & comment create/update federation - Improve checks to ensure we don't federate when we don't need to
385 lines
15 KiB
Rust
385 lines
15 KiB
Rust
use crate::{Action, Context, Error, KeyOwner, OnBehalfOf, RecoverableError, Required};
|
|
use activitystreams::{base::AnyBase, prelude::*};
|
|
use url::Url;
|
|
|
|
mod application;
|
|
mod flag;
|
|
mod like;
|
|
mod note;
|
|
mod person;
|
|
|
|
use application::{
|
|
application, create_application, delete_application,
|
|
follow::{
|
|
accept_follow_application, follow_application, reject_follow_application,
|
|
undo_accept_follow_application, undo_follow_application,
|
|
},
|
|
update_application,
|
|
};
|
|
use flag::flag;
|
|
use like::{like, undo_like};
|
|
use note::{
|
|
announce_delete_note, announce_note, announce_update_note, create_note, delete_note, note,
|
|
update_note,
|
|
};
|
|
use person::{
|
|
block::{block_person, undo_block_person},
|
|
create_person, delete_person,
|
|
follow::{
|
|
accept_follow_person, follow_person, reject_follow_person, undo_accept_follow_person,
|
|
undo_follow_person,
|
|
},
|
|
person, update_person,
|
|
};
|
|
|
|
fn log_unsupported(path: &[&str]) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
let activity = path.join(" ");
|
|
log::warn!("Attempted to ingest unsupported activity: {}", activity);
|
|
Err(Error::Invalid)
|
|
}
|
|
|
|
pub(crate) fn ingest(
|
|
any_base: AnyBase,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
mut stack: Vec<AnyBase>,
|
|
) -> Result<(), Error> {
|
|
log::debug!("Ingest");
|
|
|
|
if any_base.kind_str().is_none() {
|
|
log::debug!("Spawining download for URL");
|
|
ctx.spawner.download_apub(
|
|
OnBehalfOf::Server,
|
|
any_base.id().req("id from anybase")?.to_owned(),
|
|
stack,
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
match parse(any_base.clone(), key_owner, ctx)? {
|
|
Ok(action) => {
|
|
if let Some(outbound) = action.perform(ctx)? {
|
|
ctx.deliver(&*outbound);
|
|
}
|
|
if let Some(any_base) = stack.pop() {
|
|
ctx.spawner.process(any_base, stack);
|
|
}
|
|
}
|
|
Err(RecoverableError::MissingApub(url)) => {
|
|
log::debug!("{} needed before further processing", url);
|
|
stack.push(any_base);
|
|
ctx.spawner.download_apub(OnBehalfOf::Server, url, stack);
|
|
}
|
|
Err(RecoverableError::MissingImages(urls)) => {
|
|
stack.push(any_base);
|
|
ctx.spawner.download_images(urls, stack);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn get_obj<T, Kind>(
|
|
any_base: AnyBase,
|
|
ctx: &Context,
|
|
f: impl FnOnce(T, &Context, AnyBase) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>
|
|
where
|
|
T: ActorAndObjectRefExt + ExtendsExt<Kind>,
|
|
<T as activitystreams::base::Extends<Kind>>::Error: From<serde_json::Error>,
|
|
Error: From<<T as activitystreams::base::Extends<Kind>>::Error>,
|
|
for<'de> Kind: serde::Deserialize<'de>,
|
|
Kind: serde::Serialize,
|
|
{
|
|
wrap(any_base, ctx, |activity: T, ctx| {
|
|
let object = activity.object().as_one().req("object from activity")?;
|
|
let any_base = if object.kind_str().is_none() {
|
|
let id = object.id().req("id from object")?;
|
|
if ctx.apub.deleted(id)? {
|
|
log::warn!("Attempting to ingest deleted activity");
|
|
return Err(Error::Invalid);
|
|
}
|
|
match ctx.apub.object(id)? {
|
|
Some(any_base) => any_base,
|
|
None => return Ok(Err(RecoverableError::MissingApub(id.to_owned()))),
|
|
}
|
|
} else {
|
|
object.clone()
|
|
};
|
|
|
|
(f)(activity, ctx, any_base.clone())
|
|
})
|
|
}
|
|
|
|
fn wrap<T, Kind>(
|
|
any_base: AnyBase,
|
|
ctx: &Context,
|
|
f: impl FnOnce(T, &Context) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error>
|
|
where
|
|
T: ExtendsExt<Kind>,
|
|
<T as activitystreams::base::Extends<Kind>>::Error: From<serde_json::Error>,
|
|
Error: From<<T as activitystreams::base::Extends<Kind>>::Error>,
|
|
for<'de> Kind: serde::Deserialize<'de>,
|
|
Kind: serde::Serialize,
|
|
{
|
|
let activity: T = any_base.clone().extend()?.req("activity from any_base")?;
|
|
|
|
let res = (f)(activity, ctx);
|
|
if res.as_ref().map(|r| r.is_ok()).unwrap_or(false) {
|
|
ctx.apub.store_object(&any_base)?;
|
|
}
|
|
res
|
|
}
|
|
|
|
fn parse(
|
|
any_base: AnyBase,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Accept" => get_obj(any_base, ctx, |accept, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Follow" => get_obj(any_base, ctx, |follow, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
accept_follow_application(
|
|
&accept,
|
|
&follow,
|
|
&application,
|
|
key_owner,
|
|
ctx,
|
|
)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
accept_follow_person(&accept, &follow, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Accept", "Follow", kind]),
|
|
}
|
|
}),
|
|
kind => log_unsupported(&["Accept", kind]),
|
|
}
|
|
}),
|
|
"Announce" => get_obj(any_base, ctx, |announce, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Note" => wrap(any_base, ctx, |note, ctx| {
|
|
announce_note(&announce, ¬e, key_owner, ctx)
|
|
}),
|
|
"Update" => get_obj(any_base, ctx, |update, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Note" => wrap(any_base, ctx, |note, ctx| {
|
|
announce_update_note(&announce, &update, ¬e, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Announce", "Update", kind]),
|
|
}
|
|
}),
|
|
"Delete" => get_obj(any_base, ctx, |delete, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Note" => wrap(any_base, ctx, |note, ctx| {
|
|
announce_delete_note(&announce, &delete, ¬e, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Announce", "Delete", kind]),
|
|
}
|
|
}),
|
|
kind => log_unsupported(&["Announce", kind]),
|
|
}
|
|
}),
|
|
"Application" => wrap(any_base, ctx, |application_obj, ctx| {
|
|
application(&application_obj, key_owner, ctx)
|
|
}),
|
|
"Block" => get_obj(any_base, ctx, |block, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
block_person(&block, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Block", kind]),
|
|
}
|
|
}),
|
|
"Create" => get_obj(any_base, ctx, |create, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
create_application(&create, &application, key_owner, ctx)
|
|
}),
|
|
"Note" => wrap(any_base, ctx, |note, ctx| {
|
|
create_note(&create, ¬e, key_owner, ctx)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
create_person(&create, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Create", kind]),
|
|
}
|
|
}),
|
|
"Delete" => get_obj(any_base, ctx, |delete, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
delete_application(&delete, &application, key_owner, ctx)
|
|
}),
|
|
"Note" => wrap(any_base, ctx, |note, ctx| {
|
|
delete_note(&delete, ¬e, key_owner, ctx)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
delete_person(&delete, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Delete", kind]),
|
|
}
|
|
}),
|
|
"Flag" => wrap(any_base, ctx, |flag_obj, ctx| {
|
|
flag(&flag_obj, key_owner, ctx)
|
|
}),
|
|
"Follow" => get_obj(any_base, ctx, |follow, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
follow_application(&follow, &application, key_owner, ctx)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
follow_person(&follow, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Follow", kind]),
|
|
}
|
|
}),
|
|
"Like" => wrap(any_base, ctx, |like_obj, ctx| {
|
|
like(&like_obj, key_owner, ctx)
|
|
}),
|
|
"Note" => wrap(any_base, ctx, |note_obj, ctx| {
|
|
note(¬e_obj, key_owner, ctx)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person_obj, ctx| {
|
|
person(&person_obj, key_owner, ctx)
|
|
}),
|
|
"Reject" => get_obj(any_base, ctx, |reject, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Follow" => get_obj(any_base, ctx, |follow, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
reject_follow_application(
|
|
&reject,
|
|
&follow,
|
|
&application,
|
|
key_owner,
|
|
ctx,
|
|
)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
reject_follow_person(&reject, &follow, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Reject", "Follow", kind]),
|
|
}
|
|
}),
|
|
kind => log_unsupported(&["Reject", kind]),
|
|
}
|
|
}),
|
|
"Undo" => get_obj(any_base, ctx, |undo, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Accept" => get_obj(any_base, ctx, |accept, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Follow" => get_obj(any_base, ctx, |follow, ctx, any_base| match any_base
|
|
.kind_str()
|
|
.req("kind")?
|
|
{
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
undo_accept_follow_application(
|
|
&undo,
|
|
&accept,
|
|
&follow,
|
|
&application,
|
|
key_owner,
|
|
ctx,
|
|
)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
undo_accept_follow_person(
|
|
&undo, &accept, &follow, &person, key_owner, ctx,
|
|
)
|
|
}),
|
|
kind => log_unsupported(&["Undo", "Accept", "Follow", kind]),
|
|
}),
|
|
kind => log_unsupported(&["Undo", "Accept", kind]),
|
|
}
|
|
}),
|
|
"Block" => get_obj(any_base, ctx, |block, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
undo_block_person(&undo, &block, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Undo", "Block", kind]),
|
|
}
|
|
}),
|
|
"Follow" => get_obj(any_base, ctx, |follow, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
undo_follow_application(&undo, &follow, &application, key_owner, ctx)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
undo_follow_person(&undo, &follow, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Undo", "Follow", kind]),
|
|
}
|
|
}),
|
|
"Like" => wrap(any_base, ctx, |like, ctx| {
|
|
undo_like(&undo, &like, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Undo", kind]),
|
|
}
|
|
}),
|
|
"Update" => get_obj(any_base, ctx, |update, ctx, any_base| {
|
|
match any_base.kind_str().req("kind")? {
|
|
"Application" => wrap(any_base, ctx, |application, ctx| {
|
|
update_application(&update, &application, key_owner, ctx)
|
|
}),
|
|
"Note" => wrap(any_base, ctx, |note, ctx| {
|
|
update_note(&update, ¬e, key_owner, ctx)
|
|
}),
|
|
"Person" => wrap(any_base, ctx, |person, ctx| {
|
|
update_person(&update, &person, key_owner, ctx)
|
|
}),
|
|
kind => log_unsupported(&["Update", kind]),
|
|
}
|
|
}),
|
|
kind => log_unsupported(&[kind]),
|
|
}
|
|
}
|
|
|
|
fn require_federation(
|
|
key_owner: Option<KeyOwner>,
|
|
actor: &Url,
|
|
ctx: &Context,
|
|
) -> Result<(), Error> {
|
|
if let Some(key_owner) = key_owner {
|
|
if !ctx.is_or_is_server_of(key_owner, actor)? {
|
|
log::debug!("Key Owner does not control Actor");
|
|
return Err(Error::Invalid);
|
|
}
|
|
}
|
|
|
|
let domain = actor.domain().req("domain from actor")?;
|
|
|
|
if !ctx.store.is_domain_federated(domain)? {
|
|
log::debug!("Domain is not federated");
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn require_not_blocked(
|
|
key_owner: Option<KeyOwner>,
|
|
actor: &Url,
|
|
ctx: &Context,
|
|
) -> Result<(), Error> {
|
|
if let Some(key_owner) = key_owner {
|
|
if !ctx.is_or_is_server_of(key_owner, actor)? {
|
|
log::debug!("Key Owner does not control Actor");
|
|
return Err(Error::Invalid);
|
|
}
|
|
}
|
|
|
|
let domain = actor.domain().req("domain from actor")?;
|
|
|
|
if ctx.store.is_domain_blocked(domain)? {
|
|
log::debug!("Domain is blocked");
|
|
return Err(Error::Blocked);
|
|
}
|
|
|
|
Ok(())
|
|
}
|