From a3ec1e7b58286c497549aac0936983fee027a02a Mon Sep 17 00:00:00 2001 From: silverpill Date: Sun, 27 Nov 2022 22:03:45 +0000 Subject: [PATCH] Verify that actor alias exists before moving local followers --- src/activitypub/handlers/move_person.rs | 2 +- src/mastodon_api/accounts/views.rs | 56 ++++++++++++++++++++----- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/activitypub/handlers/move_person.rs b/src/activitypub/handlers/move_person.rs index 329df70..c9ae141 100644 --- a/src/activitypub/handlers/move_person.rs +++ b/src/activitypub/handlers/move_person.rs @@ -87,7 +87,7 @@ pub async fn handle_move_person( .map_err(|_| ValidationError("invalid alias list"))?; aliases.extend(also_known_as); }; - if !aliases.iter().any(|actor_id| actor_id == &old_actor_id) { + if !aliases.contains(&old_actor_id) { return Err(ValidationError("target ID is not an alias").into()); }; diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index c801906..c1ae6d9 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -56,6 +56,7 @@ use crate::models::profiles::queries::{ get_profile_by_id, get_profile_by_remote_actor_id, search_profiles_by_did, + search_profiles_by_did_only, update_profile, }; use crate::models::profiles::types::{ @@ -273,7 +274,8 @@ async fn move_followers( ) -> Result { let db_client = &mut **get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; - // Old profile could be deleted + // Existence of actor is not verified because + // the old profile could have been deleted let maybe_from_profile = match get_profile_by_remote_actor_id( db_client, &request_data.from_actor_id, @@ -282,6 +284,26 @@ async fn move_followers( Err(DatabaseError::NotFound(_)) => None, Err(other_error) => return Err(other_error.into()), }; + if maybe_from_profile.is_some() { + // Find known aliases of the current user + let mut aliases = vec![]; + for identity_proof in current_user.profile.identity_proofs.inner() { + let profiles = search_profiles_by_did_only( + db_client, + &identity_proof.issuer, + ).await?; + for profile in profiles { + if profile.id == current_user.id { + continue; + }; + let actor_id = profile.actor_id(&config.instance_url()); + aliases.push(actor_id); + }; + }; + if !aliases.contains(&request_data.from_actor_id) { + return Err(ValidationError("old profile is not an alias").into()); + }; + }; let mut followers = vec![]; for follower_address in request_data.followers_csv.lines() { let follower_acct = ActorAddress::from_str(follower_address)? @@ -292,19 +314,33 @@ async fn move_followers( // Add remote actor to activity recipients list followers.push(remote_actor.id); } else { - // Immediately move local followers + // Immediately move local followers (only if alias can be verified) if let Some(ref from_profile) = maybe_from_profile { match unfollow(db_client, &follower.id, &from_profile.id).await { - Ok(_) => (), - Err(DatabaseError::NotFound(_)) => (), + Ok(maybe_follow_request_id) => { + // Send Undo(Follow) to a remote actor + let remote_actor = from_profile.actor_json.as_ref() + .expect("actor data must be present"); + let follow_request_id = maybe_follow_request_id + .expect("follow request must exist"); + // TODO: send in a batch + prepare_undo_follow( + &config.instance(), + ¤t_user, + remote_actor, + &follow_request_id, + ).spawn_deliver(); + }, + // Not a follower, ignore + Err(DatabaseError::NotFound(_)) => continue, + Err(other_error) => return Err(other_error.into()), + }; + match follow(db_client, &follower.id, ¤t_user.id).await { + Ok(_) => (), + // Ignore if already following + Err(DatabaseError::AlreadyExists(_)) => (), Err(other_error) => return Err(other_error.into()), }; - }; - match follow(db_client, &follower.id, ¤t_user.id).await { - Ok(_) => (), - // Ignore if already following - Err(DatabaseError::AlreadyExists(_)) => (), - Err(other_error) => return Err(other_error.into()), }; }; };