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
275 lines
8.9 KiB
Rust
275 lines
8.9 KiB
Rust
use crate::{
|
|
apub::{
|
|
actions::{
|
|
apub::require_federation, AcceptFollowRequest, CreateFollowRequest,
|
|
RejectFollowRequest, UndoAcceptFollow, UndoFollow, UndoFollowRequest,
|
|
},
|
|
ExtendedPerson,
|
|
},
|
|
recover,
|
|
store::view::Relationship,
|
|
Action, Context, Error, KeyOwner, RecoverableError, Required,
|
|
};
|
|
use activitystreams::prelude::*;
|
|
use chrono::Utc;
|
|
|
|
fn validate_follow_person(
|
|
follow: &activitystreams::activity::Follow,
|
|
person: &ExtendedPerson,
|
|
relationship: Relationship,
|
|
ctx: &Context,
|
|
) -> Result<Result<(), RecoverableError>, Error> {
|
|
let follow_actor = follow.actor()?.as_single_id().req("follow actor id")?;
|
|
let follow_actor = recover!(follow_actor, ctx.apub.id_for_apub(follow_actor)?);
|
|
let follow_actor = follow_actor
|
|
.profile()
|
|
.req("follow actor id as profile id")?;
|
|
|
|
let follow_object = person.id_unchecked().req("person id")?;
|
|
let follow_object = recover!(follow_object, ctx.apub.id_for_apub(follow_object)?);
|
|
let follow_object = follow_object.profile().req("person id as profile id")?;
|
|
|
|
if relationship.left != follow_object || relationship.right != follow_actor {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
Ok(Ok(()))
|
|
}
|
|
|
|
pub(crate) fn follow_person(
|
|
follow: &activitystreams::activity::Follow,
|
|
person: &ExtendedPerson,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
let follow_id = follow.id_unchecked().req("follow id")?;
|
|
|
|
// Double create
|
|
if ctx.apub.object(follow_id)?.is_some() {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let published = follow
|
|
.published()
|
|
.map(|p| p.into())
|
|
.unwrap_or_else(|| Utc::now());
|
|
|
|
let actor_id = follow.actor()?.as_single_id().req("follow actor id")?;
|
|
require_federation(key_owner, actor_id, ctx)?;
|
|
|
|
let object_id = person.id_unchecked().req("person id")?;
|
|
|
|
let actor = recover!(actor_id, ctx.apub.id_for_apub(actor_id)?);
|
|
let actor = actor.profile().req("follow actor id as profile id")?;
|
|
|
|
if let Some(actor_profile) = ctx.store.profiles.by_id(actor)? {
|
|
if actor_profile.is_suspended() {
|
|
return Err(Error::Invalid);
|
|
}
|
|
} else {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let object = recover!(object_id, ctx.apub.id_for_apub(object_id)?);
|
|
let object = object.profile().req("person id as profile id")?;
|
|
|
|
Ok(Ok(Box::new(CreateFollowRequest {
|
|
follow_apub_id: Some(follow_id.to_owned()),
|
|
followed_profile: object,
|
|
followed_by_profile: actor,
|
|
published,
|
|
})))
|
|
}
|
|
|
|
pub(crate) fn reject_follow_person(
|
|
reject: &activitystreams::activity::Reject,
|
|
follow: &activitystreams::activity::Follow,
|
|
person: &ExtendedPerson,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
let reject_actor = reject.actor()?.as_single_id().req("reject actor id")?;
|
|
require_federation(key_owner, reject_actor, ctx)?;
|
|
|
|
let follow_object = person.id_unchecked().req("person id")?;
|
|
|
|
if follow_object != reject_actor {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let actor = recover!(reject_actor, ctx.apub.id_for_apub(reject_actor)?);
|
|
let actor = actor.profile().req("reject actor id as profile id")?;
|
|
if let Some(actor_profile) = ctx.store.profiles.by_id(actor)? {
|
|
if actor_profile.is_suspended() {
|
|
return Err(Error::Invalid);
|
|
}
|
|
} else {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let follow_id = follow.id_unchecked().req("follow id")?;
|
|
let id = recover!(follow_id, ctx.apub.id_for_apub(follow_id)?);
|
|
let id = id.follow_request().req("follow id as follow request id")?;
|
|
|
|
let follow_request = ctx
|
|
.store
|
|
.view
|
|
.follow_requests
|
|
.by_id(id)?
|
|
.req("follow request by id")?;
|
|
if let Err(e) = validate_follow_person(follow, person, follow_request, ctx)? {
|
|
return Ok(Err(e));
|
|
}
|
|
|
|
Ok(Ok(Box::new(RejectFollowRequest {
|
|
follow_request_id: id,
|
|
})))
|
|
}
|
|
|
|
pub(crate) fn accept_follow_person(
|
|
accept: &activitystreams::activity::Accept,
|
|
follow: &activitystreams::activity::Follow,
|
|
person: &ExtendedPerson,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
let accept_actor = accept.actor()?.as_single_id().req("accept actor id")?;
|
|
require_federation(key_owner, accept_actor, ctx)?;
|
|
|
|
let follow_object = person.id_unchecked().req("person id")?;
|
|
|
|
if follow_object != accept_actor {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let actor = recover!(accept_actor, ctx.apub.id_for_apub(accept_actor)?);
|
|
let actor = actor.profile().req("accept actor id as profile id")?;
|
|
if let Some(actor_profile) = ctx.store.profiles.by_id(actor)? {
|
|
if actor_profile.is_suspended() {
|
|
return Err(Error::Invalid);
|
|
}
|
|
} else {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let follow_id = follow.id_unchecked().req("follow id")?;
|
|
let accept_id = accept.id_unchecked().req("accept id")?;
|
|
let published = accept
|
|
.published()
|
|
.map(|p| p.into())
|
|
.unwrap_or_else(|| Utc::now());
|
|
|
|
let id = recover!(follow_id, ctx.apub.id_for_apub(follow_id)?);
|
|
let id = id.follow_request().req("follow id as follow request id")?;
|
|
|
|
let follow_request = ctx
|
|
.store
|
|
.view
|
|
.follow_requests
|
|
.by_id(id)?
|
|
.req("follow request by id")?;
|
|
if let Err(e) = validate_follow_person(follow, person, follow_request, ctx)? {
|
|
return Ok(Err(e));
|
|
}
|
|
|
|
Ok(Ok(Box::new(AcceptFollowRequest {
|
|
accept_apub_id: Some(accept_id.to_owned()),
|
|
follow_request_id: id,
|
|
published,
|
|
})))
|
|
}
|
|
|
|
pub(crate) fn undo_follow_person(
|
|
undo: &activitystreams::activity::Undo,
|
|
follow: &activitystreams::activity::Follow,
|
|
person: &ExtendedPerson,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
let undo_actor = undo.actor()?.as_single_id().req("undo actor id")?;
|
|
require_federation(key_owner, undo_actor, ctx)?;
|
|
|
|
let follow_actor = follow.actor()?.as_single_id().req("follow actor id")?;
|
|
|
|
if follow_actor != undo_actor {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let actor = recover!(undo_actor, ctx.apub.id_for_apub(undo_actor)?);
|
|
let actor = actor.profile().req("undo actor id as profile id")?;
|
|
if let Some(actor_profile) = ctx.store.profiles.by_id(actor)? {
|
|
if actor_profile.is_suspended() {
|
|
return Err(Error::Invalid);
|
|
}
|
|
} else {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let follow_actor = recover!(follow_actor, ctx.apub.id_for_apub(follow_actor)?);
|
|
let follow_actor = follow_actor
|
|
.profile()
|
|
.req("follow actor id as profile id")?;
|
|
|
|
let follow_object_id = person.id_unchecked().req("person id")?;
|
|
let follow_object = recover!(follow_object_id, ctx.apub.id_for_apub(follow_object_id)?);
|
|
let follow_object = follow_object.profile().req("person id as profile id")?;
|
|
|
|
let follow_id_opt = ctx
|
|
.store
|
|
.view
|
|
.follows
|
|
.by_forward(follow_object, follow_actor)?;
|
|
|
|
if let Some(follow_id) = follow_id_opt {
|
|
return Ok(Ok(Box::new(UndoFollow { follow_id })));
|
|
}
|
|
|
|
let follow_request_id = ctx
|
|
.store
|
|
.view
|
|
.follow_requests
|
|
.by_forward(follow_object, follow_actor)?
|
|
.req("follow request by forward")?;
|
|
|
|
Ok(Ok(Box::new(UndoFollowRequest { follow_request_id })))
|
|
}
|
|
|
|
pub(crate) fn undo_accept_follow_person(
|
|
undo: &activitystreams::activity::Undo,
|
|
accept: &activitystreams::activity::Accept,
|
|
follow: &activitystreams::activity::Follow,
|
|
person: &ExtendedPerson,
|
|
key_owner: Option<KeyOwner>,
|
|
ctx: &Context,
|
|
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
|
|
let undo_actor = undo.actor()?.as_single_id().req("undo actor id")?;
|
|
require_federation(key_owner, undo_actor, ctx)?;
|
|
|
|
let accept_actor = accept.actor()?.as_single_id().req("accept actor id")?;
|
|
|
|
if accept_actor != undo_actor {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let actor = recover!(undo_actor, ctx.apub.id_for_apub(undo_actor)?);
|
|
let actor = actor.profile().req("undo actor id as profile id")?;
|
|
if let Some(actor_profile) = ctx.store.profiles.by_id(actor)? {
|
|
if actor_profile.is_suspended() {
|
|
return Err(Error::Invalid);
|
|
}
|
|
} else {
|
|
return Err(Error::Invalid);
|
|
}
|
|
|
|
let accept_id = accept.id_unchecked().req("accept id")?;
|
|
let id = recover!(accept_id, ctx.apub.id_for_apub(accept_id)?);
|
|
let id = id.follow().req("accept id as follow id")?;
|
|
|
|
let relation = ctx.store.view.follows.by_id(id)?.req("follow by id")?;
|
|
if let Err(e) = validate_follow_person(follow, person, relation, ctx)? {
|
|
return Ok(Err(e));
|
|
}
|
|
|
|
Ok(Ok(Box::new(UndoAcceptFollow { follow_id: id })))
|
|
}
|