hyaenidae/profiles/src/apub/actions/apub/follow.rs
asonix 29bdf064e9 Profiles: Update profile delete to profile suspend
Clear profile data on suspend
Clear comment body on delete
Update Unfollow and Unblock operations to only delete apub IDs if present
2021-01-14 20:41:53 -06:00

230 lines
7.2 KiB
Rust

use crate::{
apub::actions::{
AcceptFollowRequest, CreateFollowRequest, RejectFollowRequest, UndoAcceptFollow,
UndoFollow, UndoFollowRequest,
},
recover,
store::view::Relationship,
Action, Context, Error, RecoverableError, Required,
};
use activitystreams::prelude::*;
fn validate_follow(
follow: &activitystreams::activity::Follow,
relationship: Relationship,
ctx: &Context,
) -> Result<Result<(), RecoverableError>, Error> {
let follow_actor = follow.actor()?.as_single_id().req()?;
let follow_actor = recover!(follow_actor, ctx.apub.id_for_apub(follow_actor)?);
let follow_actor = follow_actor.profile().req()?;
let follow_object = follow.object().as_single_id().req()?;
let follow_object = recover!(follow_object, ctx.apub.id_for_apub(follow_object)?);
let follow_object = follow_object.profile().req()?;
if relationship.left != follow_object || relationship.right != follow_actor {
return Err(Error::Invalid);
}
Ok(Ok(()))
}
pub(crate) fn follow(
follow: &activitystreams::activity::Follow,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let follow_id = follow.id_unchecked().req()?;
// Double create
if ctx.apub.object(follow_id)?.is_some() {
return Err(Error::Invalid);
}
let published = follow.published().req()?;
let actor_id = follow.actor()?.as_single_id().req()?;
let object_id = follow.object().as_single_id().req()?;
let actor = recover!(actor_id, ctx.apub.id_for_apub(actor_id)?);
let actor = actor.profile().req()?;
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()?;
Ok(Ok(Box::new(CreateFollowRequest {
follow_apub_id: Some(follow_id.to_owned()),
followed_profile: object,
followed_by_profile: actor,
published: published.into(),
})))
}
pub(crate) fn reject_follow(
reject: &activitystreams::activity::Reject,
follow: &activitystreams::activity::Follow,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let reject_actor = reject.actor()?.as_single_id().req()?;
let follow_object = follow.object().as_single_id().req()?;
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()?;
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()?;
let id = recover!(follow_id, ctx.apub.id_for_apub(follow_id)?);
let id = id.follow_request().req()?;
let follow_request = ctx.store.view.follow_requests.by_id(id)?.req()?;
if let Err(e) = validate_follow(follow, follow_request, ctx)? {
return Ok(Err(e));
}
Ok(Ok(Box::new(RejectFollowRequest {
follow_request_id: id,
})))
}
pub(crate) fn accept_follow(
accept: &activitystreams::activity::Accept,
follow: &activitystreams::activity::Follow,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let accept_actor = accept.actor()?.as_single_id().req()?;
let follow_object = follow.object().as_single_id().req()?;
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()?;
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()?;
let accept_id = accept.id_unchecked().req()?;
let published = accept.published().req()?;
let id = recover!(follow_id, ctx.apub.id_for_apub(follow_id)?);
let id = id.follow_request().req()?;
let follow_request = ctx.store.view.follow_requests.by_id(id)?.req()?;
if let Err(e) = validate_follow(follow, 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: published.into(),
})))
}
pub(crate) fn undo_follow(
undo: &activitystreams::activity::Undo,
follow: &activitystreams::activity::Follow,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let undo_actor = undo.actor()?.as_single_id().req()?;
let follow_actor = follow.actor()?.as_single_id().req()?;
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()?;
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()?;
let follow_object_id = follow.object().as_single_id().req()?;
let follow_object = recover!(follow_object_id, ctx.apub.id_for_apub(follow_object_id)?);
let follow_object = follow_object.profile().req()?;
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()?;
Ok(Ok(Box::new(UndoFollowRequest { follow_request_id })))
}
pub(crate) fn undo_accept_follow(
undo: &activitystreams::activity::Undo,
accept: &activitystreams::activity::Accept,
follow: &activitystreams::activity::Follow,
ctx: &Context,
) -> Result<Result<Box<dyn Action>, RecoverableError>, Error> {
let undo_actor = undo.actor()?.as_single_id().req()?;
let accept_actor = accept.actor()?.as_single_id().req()?;
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()?;
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()?;
let id = recover!(accept_id, ctx.apub.id_for_apub(accept_id)?);
let id = id.follow().req()?;
let relation = ctx.store.view.follows.by_id(id)?.req()?;
if let Err(e) = validate_follow(follow, relation, ctx)? {
return Ok(Err(e));
}
Ok(Ok(Box::new(UndoAcceptFollow { follow_id: id })))
}