asonix
29bdf064e9
Clear profile data on suspend Clear comment body on delete Update Unfollow and Unblock operations to only delete apub IDs if present
230 lines
7.2 KiB
Rust
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 })))
|
|
}
|