hyaenidae/profiles/src/apub/actions/apub/application/follow.rs
asonix 83b447b780 Profiles: Federation fixes
- Fix image federation
- Enable submission & comment create/update federation
- Improve checks to ensure we don't federate when we don't need to
2021-01-17 15:01:45 -06:00

232 lines
7.6 KiB
Rust

use crate::{
apub::{
actions::{
apub::require_not_blocked, AcceptFederationRequest, CreateFederationRequest,
RejectFederationRequest, UndoAcceptFederation, UndoFederation, UndoFederationRequest,
},
ExtendedApplication,
},
recover,
store::view::Relationship,
Action, Context, Error, KeyOwner, RecoverableError, Required,
};
use activitystreams::prelude::*;
use chrono::Utc;
fn validate_follow_application(
follow: &activitystreams::activity::Follow,
application: &ExtendedApplication,
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.server().req("follow actor as server id")?;
let follow_object = application.id_unchecked().req("application id")?;
let follow_object = recover!(follow_object, ctx.apub.id_for_apub(follow_object)?);
let follow_object = follow_object.server().req("application id as server id")?;
if relationship.left != follow_object || relationship.right != follow_actor {
return Err(Error::Invalid);
}
Ok(Ok(()))
}
pub(crate) fn follow_application(
follow: &activitystreams::activity::Follow,
application: &ExtendedApplication,
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_not_blocked(key_owner, actor_id, ctx)?;
let object_id = application.id_unchecked().req("application id")?;
let actor = recover!(actor_id, ctx.apub.id_for_apub(actor_id)?);
let actor = actor.server().req("follow actor id as server id")?;
let object = recover!(object_id, ctx.apub.id_for_apub(object_id)?);
let object = object.server().req("application id as server id")?;
Ok(Ok(Box::new(CreateFederationRequest {
follow_apub_id: Some(follow_id.to_owned()),
followed_server: object,
followed_by_server: actor,
published,
})))
}
pub(crate) fn reject_follow_application(
reject: &activitystreams::activity::Reject,
follow: &activitystreams::activity::Follow,
application: &ExtendedApplication,
key_owner: Option<KeyOwner>,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let reject_actor = reject.actor()?.as_single_id().req("reject actor")?;
require_not_blocked(key_owner, reject_actor, ctx)?;
let follow_object = application.id_unchecked().req("application id")?;
if follow_object != reject_actor {
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
.server_follow_request()
.req("follow id as server follow request")?;
let follow_request = ctx
.store
.view
.server_follow_requests
.by_id(id)?
.req("server follow request")?;
if let Err(e) = validate_follow_application(follow, application, follow_request, ctx)? {
return Ok(Err(e));
}
Ok(Ok(Box::new(RejectFederationRequest {
follow_request_id: id,
})))
}
pub(crate) fn accept_follow_application(
accept: &activitystreams::activity::Accept,
follow: &activitystreams::activity::Follow,
application: &ExtendedApplication,
key_owner: Option<KeyOwner>,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let accept_actor = accept.actor()?.as_single_id().req("accept actor")?;
require_not_blocked(key_owner, accept_actor, ctx)?;
let follow_object = application.id_unchecked().req("application id")?;
if follow_object != accept_actor {
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
.server_follow_request()
.req("follow id as server follow request id")?;
let follow_request = ctx
.store
.view
.server_follow_requests
.by_id(id)?
.req("server follow request by id")?;
if let Err(e) = validate_follow_application(follow, application, follow_request, ctx)? {
return Ok(Err(e));
}
Ok(Ok(Box::new(AcceptFederationRequest {
accept_apub_id: Some(accept_id.to_owned()),
follow_request_id: id,
published,
})))
}
pub(crate) fn undo_follow_application(
undo: &activitystreams::activity::Undo,
follow: &activitystreams::activity::Follow,
application: &ExtendedApplication,
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_not_blocked(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 follow_actor = recover!(follow_actor, ctx.apub.id_for_apub(follow_actor)?);
let follow_actor = follow_actor.server().req("follow actor id as server id")?;
let follow_object_id = application.id_unchecked().req("application id")?;
let follow_object = recover!(follow_object_id, ctx.apub.id_for_apub(follow_object_id)?);
let follow_object = follow_object.server().req("application id as server id")?;
let follow_id_opt = ctx
.store
.view
.server_follows
.by_forward(follow_object, follow_actor)?;
if let Some(follow_id) = follow_id_opt {
return Ok(Ok(Box::new(UndoFederation { follow_id })));
}
let follow_request_id = ctx
.store
.view
.server_follow_requests
.by_forward(follow_object, follow_actor)?
.req("follow request id by forward")?;
Ok(Ok(Box::new(UndoFederationRequest { follow_request_id })))
}
pub(crate) fn undo_accept_follow_application(
undo: &activitystreams::activity::Undo,
accept: &activitystreams::activity::Accept,
follow: &activitystreams::activity::Follow,
application: &ExtendedApplication,
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_not_blocked(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 accept_id = accept.id_unchecked().req("accept id")?;
let id = recover!(accept_id, ctx.apub.id_for_apub(accept_id)?);
let id = id.server_follow().req("accept id as server follow id")?;
let relation = ctx
.store
.view
.server_follows
.by_id(id)?
.req("server follow by id")?;
if let Err(e) = validate_follow_application(follow, application, relation, ctx)? {
return Ok(Err(e));
}
Ok(Ok(Box::new(UndoAcceptFederation { follow_id: id })))
}