Handle non-nested Undo(Follow) activities

This commit is contained in:
silverpill 2022-12-13 01:12:42 +00:00
parent f2037c9516
commit 1b588a86e0
2 changed files with 55 additions and 13 deletions

View file

@ -4,7 +4,7 @@ use tokio_postgres::GenericClient;
use crate::activitypub::{ use crate::activitypub::{
identifiers::parse_local_actor_id, identifiers::parse_local_actor_id,
receiver::find_object_id, receiver::{deserialize_into_object_id, find_object_id},
vocabulary::{ANNOUNCE, FOLLOW, LIKE}, vocabulary::{ANNOUNCE, FOLLOW, LIKE},
}; };
use crate::config::Config; use crate::config::Config;
@ -23,12 +23,15 @@ use crate::models::{
delete_reaction, delete_reaction,
get_reaction_by_remote_activity_id, get_reaction_by_remote_activity_id,
}, },
relationships::queries::unfollow, relationships::queries::{
get_follow_request_by_activity_id,
unfollow,
},
}; };
use super::HandlerResult; use super::HandlerResult;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Undo { struct UndoFollow {
actor: String, actor: String,
object: Value, object: Value,
} }
@ -36,8 +39,10 @@ struct Undo {
async fn handle_undo_follow( async fn handle_undo_follow(
config: &Config, config: &Config,
db_client: &mut impl GenericClient, db_client: &mut impl GenericClient,
activity: Undo, activity: Value,
) -> HandlerResult { ) -> HandlerResult {
let activity: UndoFollow = serde_json::from_value(activity)
.map_err(|_| ValidationError("unexpected activity structure"))?;
let source_profile = get_profile_by_remote_actor_id( let source_profile = get_profile_by_remote_actor_id(
db_client, db_client,
&activity.actor, &activity.actor,
@ -58,25 +63,45 @@ async fn handle_undo_follow(
Ok(Some(FOLLOW)) Ok(Some(FOLLOW))
} }
#[derive(Deserialize)]
struct Undo {
actor: String,
#[serde(deserialize_with = "deserialize_into_object_id")]
object: String,
}
pub async fn handle_undo( pub async fn handle_undo(
config: &Config, config: &Config,
db_client: &mut impl GenericClient, db_client: &mut impl GenericClient,
activity: Value, activity: Value,
) -> HandlerResult { ) -> HandlerResult {
let activity: Undo = serde_json::from_value(activity) if let Some(FOLLOW) = activity["object"]["type"].as_str() {
.map_err(|_| ValidationError("unexpected activity structure"))?; // Undo() with nested follow activity
if let Some(FOLLOW) = activity.object["type"].as_str() { return handle_undo_follow(config, db_client, activity).await;
// Object type is currently required for processing Undo(Follow)
// because activity IDs of remote follow requests are not stored.
return handle_undo_follow(config, db_client, activity).await
}; };
let activity: Undo = serde_json::from_value(activity)
.map_err(|_| ValidationError("unexpected activity structure"))?;
let actor_profile = get_profile_by_remote_actor_id( let actor_profile = get_profile_by_remote_actor_id(
db_client, db_client,
&activity.actor, &activity.actor,
).await?; ).await?;
let object_id = find_object_id(&activity.object)?;
match get_reaction_by_remote_activity_id(db_client, &object_id).await { match get_follow_request_by_activity_id(db_client, &activity.object).await {
Ok(follow_request) => {
// Undo(Follow)
unfollow(
db_client,
&follow_request.source_id,
&follow_request.target_id,
).await?;
return Ok(Some(FOLLOW));
},
Err(DatabaseError::NotFound(_)) => (), // try other object types
Err(other_error) => return Err(other_error.into()),
};
match get_reaction_by_remote_activity_id(db_client, &activity.object).await {
Ok(reaction) => { Ok(reaction) => {
// Undo(Like) // Undo(Like)
if reaction.author_id != actor_profile.id { if reaction.author_id != actor_profile.id {
@ -93,7 +118,7 @@ pub async fn handle_undo(
// Undo(Announce) // Undo(Announce)
let post = match get_post_by_remote_object_id( let post = match get_post_by_remote_object_id(
db_client, db_client,
&object_id, &activity.object,
).await { ).await {
Ok(post) => post, Ok(post) => post,
// Ignore undo if neither reaction nor repost is found // Ignore undo if neither reaction nor repost is found

View file

@ -272,6 +272,23 @@ pub async fn get_follow_request_by_id(
Ok(request) Ok(request)
} }
pub async fn get_follow_request_by_activity_id(
db_client: &impl GenericClient,
activity_id: &str,
) -> Result<DbFollowRequest, DatabaseError> {
let maybe_row = db_client.query_opt(
"
SELECT follow_request
FROM follow_request
WHERE activity_id = $1
",
&[&activity_id],
).await?;
let row = maybe_row.ok_or(DatabaseError::NotFound("follow request"))?;
let request = row.try_get("follow_request")?;
Ok(request)
}
pub async fn get_followers( pub async fn get_followers(
db_client: &impl GenericClient, db_client: &impl GenericClient,
profile_id: &Uuid, profile_id: &Uuid,